Merge "Various fixes to ARM rootfs scripts"
diff --git a/Android.mk b/Android.mk
index 6b5ab61..59e9806 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,3 +21,8 @@
include $(LOCAL_PATH)/host_package.mk
endif
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 4c1985d..f235841 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -66,3 +66,5 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/bin/hw/android.hardware.health.storage@1.0-service.cuttlefish)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/init/android.hardware.health.storage@1.0-service.cuttlefish.rc)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/vintf/manifest/manifest_android.hardware.health.storage@1.0.cuttlefish.xml)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/apex/com.android.gki.*)
diff --git a/README.md b/README.md
index aa63bba..2220898 100644
--- a/README.md
+++ b/README.md
@@ -30,10 +30,10 @@
3. Go to http://ci.android.com/
4. Enter a branch name. Start with `aosp-master` if you don't know what you're
looking for
-5. Navigate to `aosp_cf_x86_phone` and click on `userdebug` for the latest build
+5. Navigate to `aosp_cf_x86_64_phone` and click on `userdebug` for the latest build
6. Click on `Artifacts`
7. Scroll down to the OTA images. These packages look like
- `aosp_cf_x86_phone-img-xxxxxx.zip` -- it will always have `img` in the name.
+ `aosp_cf_x86_64_phone-img-xxxxxx.zip` -- it will always have `img` in the name.
Download this file
8. Scroll down to `cvd-host_package.tar.gz`. You should always download a host
package from the same build as your images.
@@ -43,7 +43,7 @@
mkdir cf
cd cf
tar xvf /path/to/cvd-host_package.tar.gz
- unzip /path/to/aosp_cf_x86_phone-img-xxxxxx.zip
+ unzip /path/to/aosp_cf_x86_64_phone-img-xxxxxx.zip
```
10. Launch cuttlefish with:
@@ -60,10 +60,18 @@
`$ ./bin/adb -e shell`
-## Launch Viewer
+## Launch Viewer (WebRTC)
-You can use the [TightVNC JViewer](https://www.tightvnc.com/download.php). Once
-you have downloaded the *TightVNC Java Viewer JAR in a ZIP archive*, run it with
+When launching with `---start_webrtc` (the default), you can see a list of all
+available devices at `https://localhost:8443` . For more information, see the
+WebRTC on Cuttlefish
+[documentation](https://source.android.com/setup/create/cuttlefish-ref-webrtc).
+
+## Launch Viewer (VNC)
+
+When launching with `--start_vnc_server=true` , You can use the
+[TightVNC JViewer](https://www.tightvnc.com/download.php). Once you have
+downloaded the *TightVNC Java Viewer JAR in a ZIP archive*, run it with
`$ java -jar tightvnc-jviewer.jar -ScalingFactor=50 -Tunneling=no -host=localhost -port=6444`
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 4067d27..889dfbe 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -19,6 +19,12 @@
},
{
"name": "vts_ibase_test"
+ },
+ {
+ "name": "CtsScopedStorageDeviceOnlyTest"
+ },
+ {
+ "name": "MediaProviderTests"
}
]
}
diff --git a/build/Android.bp b/build/Android.bp
index 76b8480..b824a2b 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -43,12 +43,18 @@
"config_server",
"console_forwarder",
"crosvm",
+ "cvd",
+ "cvd_internal_host_bugreport",
+ "cvd_internal_start",
+ "cvd_internal_status",
+ "cvd_internal_stop",
"cvd_host_bugreport",
"cvd_status",
"extract-ikconfig",
"extract-vmlinux",
"fsck.f2fs",
"gnss_grpc_proxy",
+ "health",
"kernel_log_monitor",
"launch_cvd",
"libgrpc++",
@@ -60,6 +66,7 @@
"lz4",
"make_f2fs",
"metrics",
+ "mkbootfs",
"mkbootimg",
"mkenvimage",
"modem_simulator",
@@ -75,8 +82,8 @@
"secure_env",
"socket_vsock_proxy",
"stop_cvd",
- "tapsetiff",
"tombstone_receiver",
+ "toybox",
"unpack_bootimg",
"vnc_server",
"webRTC",
@@ -99,6 +106,7 @@
"webrtc_controls.js",
"webrtc_cf.js",
"webrtc_index.html",
+ "webrtc_rootcanal.js",
"webrtc_server.crt",
"webrtc_server.key",
"webrtc_server.p12",
@@ -157,6 +165,12 @@
"xhci.policy_aarch64",
]
+cvd_host_qemu_bootloader = [
+ "bootloader_qemu_x86_64",
+ "bootloader_qemu_aarch64",
+ "bootloader_qemu_arm",
+]
+
cvd_host_package_customization {
name: "cvd-host_package",
deps: cvd_host_tools +
@@ -165,6 +179,7 @@
common: {
deps: cvd_host_webrtc_assets +
cvd_host_model_simulator_files +
+ cvd_host_qemu_bootloader +
cvd_bluetooth_config_files,
},
},
diff --git a/common/frontend/socket_vsock_proxy/main.cpp b/common/frontend/socket_vsock_proxy/main.cpp
index 590305b..cf68821 100644
--- a/common/frontend/socket_vsock_proxy/main.cpp
+++ b/common/frontend/socket_vsock_proxy/main.cpp
@@ -40,6 +40,10 @@
"monitor before creating a tcp-vsock tunnel."
"This option is used by --server=tcp only "
"when socket_vsock_proxy runs as a host service");
+DEFINE_int32(
+ server_fd, -1,
+ "A file descriptor. If set the passed file descriptor will be used as the "
+ "server and the corresponding port flag will be ignored");
namespace {
// Sends packets, Shutdown(SHUT_WR) on destruction
@@ -169,7 +173,14 @@
[[noreturn]] void TcpServer() {
LOG(DEBUG) << "starting TCP server on " << FLAGS_tcp_port
<< " for vsock port " << FLAGS_vsock_port;
- auto server = cuttlefish::SharedFD::SocketLocalServer(FLAGS_tcp_port, SOCK_STREAM);
+ cuttlefish::SharedFD server;
+ if (FLAGS_server_fd < 0) {
+ server =
+ cuttlefish::SharedFD::SocketLocalServer(FLAGS_tcp_port, SOCK_STREAM);
+ } else {
+ server = cuttlefish::SharedFD::Dup(FLAGS_server_fd);
+ close(FLAGS_server_fd);
+ }
CHECK(server->IsOpen()) << "Could not start server on " << FLAGS_tcp_port;
LOG(DEBUG) << "Accepting client connections";
int last_failure_reason = 0;
@@ -224,13 +235,18 @@
[[noreturn]] void VsockServer() {
LOG(DEBUG) << "Starting vsock server on " << FLAGS_vsock_port;
cuttlefish::SharedFD vsock;
- do {
- vsock = cuttlefish::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());
+ if (FLAGS_server_fd < 0) {
+ do {
+ vsock = cuttlefish::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());
+ } else {
+ vsock = cuttlefish::SharedFD::Dup(FLAGS_server_fd);
+ close(FLAGS_server_fd);
+ }
CHECK(vsock->IsOpen()) << "Could not start server on " << FLAGS_vsock_port;
while (true) {
LOG(DEBUG) << "waiting for vsock connection";
@@ -255,8 +271,11 @@
#endif
google::ParseCommandLineFlags(&argc, &argv, true);
- CHECK(FLAGS_tcp_port != 0) << "Must specify -tcp_port flag";
- CHECK(FLAGS_vsock_port != 0) << "Must specify -vsock_port flag";
+ CHECK((FLAGS_server == "tcp" && FLAGS_server_fd >= 0) || FLAGS_tcp_port != 0)
+ << "Must specify -tcp_port or -server_fd (with -server=tcp) flag";
+ CHECK((FLAGS_server == "vsock" && FLAGS_server_fd >= 0) ||
+ FLAGS_vsock_port != 0)
+ << "Must specify -vsock_port or -server_fd (with -server=vsock) flag";
if (FLAGS_adbd_events_fd >= 0) {
LOG(DEBUG) << "Wating AdbdStarted boot event from the kernel log";
diff --git a/common/libs/concurrency/multiplexer.h b/common/libs/concurrency/multiplexer.h
new file mode 100644
index 0000000..478963f
--- /dev/null
+++ b/common/libs/concurrency/multiplexer.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "common/libs/concurrency/semaphore.h"
+#include "common/libs/concurrency/thread_safe_queue.h"
+
+namespace cuttlefish {
+template <typename T, typename Queue>
+class Multiplexer {
+ public:
+ using QueuePtr = std::unique_ptr<Queue>;
+ using QueueSelector = std::function<int(void)>;
+
+ template <typename... Args>
+ static QueuePtr CreateQueue(Args&&... args) {
+ auto raw_ptr = new Queue(std::forward<Args>(args)...);
+ return QueuePtr(raw_ptr);
+ }
+
+ Multiplexer() : sem_items_{0} {}
+
+ int RegisterQueue(QueuePtr&& queue) {
+ const int id_to_return = queues_.size();
+ queues_.push_back(std::move(queue));
+ return id_to_return;
+ }
+
+ void Push(const int idx, T&& t) {
+ CheckIdx(idx);
+ queues_[idx]->Push(std::move(t));
+ sem_items_.SemPost();
+ }
+
+ T Pop(QueueSelector selector) {
+ SemWait();
+ int q_id = selector();
+ CheckIdx(q_id); // check, if weird, will die there
+ QueuePtr& queue = queues_[q_id];
+ CHECK(queue) << "queue must not be null.";
+ return queue->Pop();
+ }
+
+ T Pop() {
+ auto default_selector = [this]() -> int {
+ for (int i = 0; i < queues_.size(); i++) {
+ if (!queues_[i]->IsEmpty()) {
+ return i;
+ }
+ }
+ return -1;
+ };
+ return Pop(default_selector);
+ }
+
+ bool IsEmpty(const int idx) { return queues_[idx]->IsEmpty(); }
+
+ void SemWait() { sem_items_.SemWait(); }
+
+ private:
+ void CheckIdx(const int idx) {
+ CHECK(idx >= 0 && idx < queues_.size()) << "queues_ array out of bound";
+ }
+ // total items across the queues
+ Semaphore sem_items_;
+ std::vector<QueuePtr> queues_;
+ QueuePtr null_ptr_;
+};
+} // end of namespace cuttlefish
diff --git a/common/libs/concurrency/thread_safe_queue.h b/common/libs/concurrency/thread_safe_queue.h
index 9105299..ae16bef 100644
--- a/common/libs/concurrency/thread_safe_queue.h
+++ b/common/libs/concurrency/thread_safe_queue.h
@@ -20,6 +20,7 @@
#include <deque>
#include <iterator>
#include <mutex>
+#include <type_traits>
#include <utility>
namespace cuttlefish {
@@ -34,9 +35,11 @@
class ThreadSafeQueue {
public:
using QueueImpl = std::deque<T>;
+ using QueueFullHandler = std::function<void(QueueImpl*)>;
+
ThreadSafeQueue() = default;
explicit ThreadSafeQueue(std::size_t max_elements,
- std::function<void(QueueImpl*)> max_elements_handler)
+ QueueFullHandler max_elements_handler)
: max_elements_{max_elements},
max_elements_handler_{std::move(max_elements_handler)} {}
@@ -58,18 +61,17 @@
return std::move(items_);
}
- void Push(T&& t) {
+ template <typename U>
+ bool Push(U&& u) {
+ static_assert(std::is_assignable_v<T, decltype(u)>);
std::lock_guard<std::mutex> guard(m_);
- DropItemsIfAtCapacity();
- items_.push_back(std::move(t));
+ const bool has_room = DropItemsIfAtCapacity();
+ if (!has_room) {
+ return false;
+ }
+ items_.push_back(std::forward<U>(u));
new_item_.notify_one();
- }
-
- void Push(const T& t) {
- std::lock_guard<std::mutex> guard(m_);
- DropItemsIfAtCapacity();
- items_.push_back(t);
- new_item_.notify_one();
+ return true;
}
bool IsEmpty() {
@@ -83,15 +85,22 @@
}
private:
- void DropItemsIfAtCapacity() {
+ // return whether there's room to push
+ bool DropItemsIfAtCapacity() {
if (max_elements_ && max_elements_ == items_.size()) {
max_elements_handler_(&items_);
}
+ if (max_elements_ && max_elements_ == items_.size()) {
+ // handler intends to ignore the newly coming element or
+ // did not empty the room for whatever reason
+ return false;
+ }
+ return true;
}
std::mutex m_;
std::size_t max_elements_{};
- std::function<void(QueueImpl*)> max_elements_handler_{};
+ QueueFullHandler max_elements_handler_{};
std::condition_variable new_item_;
QueueImpl items_;
};
diff --git a/common/libs/confui/packet.cpp b/common/libs/confui/packet.cpp
index 6f6400a..6f0c3eb 100644
--- a/common/libs/confui/packet.cpp
+++ b/common/libs/confui/packet.cpp
@@ -30,8 +30,9 @@
namespace packet {
ConfUiMessage PayloadToConfUiMessage(const std::string& str_to_parse) {
auto tokens = android::base::Split(str_to_parse, ":");
- PassOrDie(tokens.size() >= 3, "PayloadToConfUiMessage takes \"", str_to_parse,
- "\" and does not have 3 tokens");
+ ConfUiCheck(tokens.size() >= 3)
+ << "PayloadToConfUiMessage takes \"" + str_to_parse + "\""
+ << "and does not have 3 tokens";
std::string msg;
std::for_each(tokens.begin() + 2, tokens.end() - 1,
[&msg](auto& token) { msg.append(token + ":"); });
@@ -109,6 +110,26 @@
return ReadPayload(fd);
}
+std::optional<std::tuple<bool, std::string>> RecvAck(
+ SharedFD fd, const std::string& session_id) {
+ auto conf_ui_msg = RecvConfUiMsg(fd);
+ if (!conf_ui_msg) {
+ ConfUiLog(ERROR) << "Received Ack failed due to communication.";
+ return std::nullopt;
+ }
+ auto [recv_session_id, type, contents] = conf_ui_msg.value();
+ if (session_id != recv_session_id) {
+ ConfUiLog(ERROR) << "Received Session ID is not the expected one,"
+ << session_id;
+ return std::nullopt;
+ }
+ if (ToCmd(type) != ConfUiCmd::kCliAck) {
+ ConfUiLog(ERROR) << "Received cmd is not ack but " << type;
+ return std::nullopt;
+ }
+ return FromCliAckCmd(contents);
+}
+
bool SendCmd(SharedFD fd, const std::string& session_id, ConfUiCmd cmd,
const std::string& additional_info) {
return WritePayload(fd, cmd, session_id, additional_info);
@@ -116,8 +137,8 @@
bool SendAck(SharedFD fd, const std::string& session_id, const bool is_success,
const std::string& additional_info) {
- return WritePayload(fd, ConfUiCmd::kCliAck, session_id,
- ToCliAckMessage(is_success, additional_info));
+ return SendCmd(fd, session_id, ConfUiCmd::kCliAck,
+ ToCliAckMessage(is_success, additional_info));
}
bool SendResponse(SharedFD fd, const std::string& session_id,
diff --git a/common/libs/confui/packet.h b/common/libs/confui/packet.h
index 848eb29..119f102 100644
--- a/common/libs/confui/packet.h
+++ b/common/libs/confui/packet.h
@@ -55,6 +55,9 @@
ConfUiMessage PayloadToConfUiMessage(const std::string& str_to_parse);
std::optional<ConfUiMessage> RecvConfUiMsg(SharedFD fd);
+std::optional<std::tuple<bool, std::string>> RecvAck(
+ SharedFD fd, const std::string& session_id);
+
bool SendAck(SharedFD fd, const std::string& session_id, const bool is_success,
const std::string& additional_info);
bool SendResponse(SharedFD fd, const std::string& session_id,
diff --git a/common/libs/confui/protocol.cpp b/common/libs/confui/protocol.cpp
index 6784034..62678d4 100644
--- a/common/libs/confui/protocol.cpp
+++ b/common/libs/confui/protocol.cpp
@@ -50,6 +50,9 @@
}
std::string ToString(const ConfUiCmd& cmd) { return ToDebugString(cmd, false); }
+std::string ToString(const ConfUiMessage& msg) {
+ return "[" + msg.session_id_ + ", " + msg.type_ + ", " + msg.msg_ + "]";
+}
ConfUiCmd ToCmd(std::uint32_t i) {
std::vector<ConfUiCmd> all_cmds{
@@ -82,6 +85,24 @@
return ConfUiCmd::kUnknown;
}
+std::optional<std::tuple<bool, std::string>> FromCliAckCmd(
+ const std::string& message) {
+ auto colon_pos = message.find_first_of(":");
+ if (colon_pos == std::string::npos) {
+ ConfUiLog(ERROR) << "Received message, \"" << message
+ << "\" is ill-formatted ";
+ return std::nullopt;
+ }
+ std::string header = message.substr(0, colon_pos);
+ std::string msg = message.substr(colon_pos + 1);
+ if (header != "error" && header != "success") {
+ ConfUiLog(ERROR) << "Received message, \"" << message
+ << "\" has a wrong header";
+ return std::nullopt;
+ }
+ return {std::tuple{(header == "success"), msg}};
+}
+
std::string ToCliAckMessage(const bool is_success, const std::string& message) {
std::string header = "error:";
if (is_success) {
diff --git a/common/libs/confui/protocol.h b/common/libs/confui/protocol.h
index 582b43e..f3b3f6e 100644
--- a/common/libs/confui/protocol.h
+++ b/common/libs/confui/protocol.h
@@ -16,7 +16,9 @@
#pragma once
#include <cstdint>
+#include <optional>
#include <string>
+#include <tuple>
namespace cuttlefish {
namespace confui {
@@ -49,6 +51,9 @@
// invalid/ignored session id
constexpr char SESSION_ANY[] = "";
+std::optional<std::tuple<bool, std::string>> FromCliAckCmd(
+ const std::string& message);
+
std::string ToCliAckMessage(const bool is_success, const std::string& message);
std::string ToCliAckErrorMsg(const std::string& message);
std::string ToCliAckSuccessMsg(const std::string& message);
@@ -58,6 +63,7 @@
std::string type_; // cmd, which cmd? ack, response, etc
std::string msg_;
};
+std::string ToString(const ConfUiMessage& msg);
} // end of namespace confui
} // end of namespace cuttlefish
diff --git a/common/libs/confui/utils.h b/common/libs/confui/utils.h
index c5e3d88..2e4ac04 100644
--- a/common/libs/confui/utils.h
+++ b/common/libs/confui/utils.h
@@ -44,29 +44,11 @@
return ArgsToStringWithDelim("", std::forward<Args>(args)...);
}
-template <typename... Args>
-std::string ToConLog(Args&&... args) {
- return ArgsToStringWithDelim(" ", "ConfUI: ", std::forward<Args>(args)...);
-}
+// note that no () surrounding LOG(level) << "ConfUI:" is crucial
+#define ConfUiLog(LOG_LEVEL) LOG(LOG_LEVEL) << "ConfUI: "
// TODO(kwstephenkim@google.com): make these look more like LOG(level)
-#define DEFINE_CONFUI_LOG_FUNC(NAME, LOG_LEVEL) \
- template <typename... Args> \
- void NAME##Log(Args&&... args) { \
- LOG(LOG_LEVEL) << ToConLog(std::forward<Args>(args)...); \
- }
-
-DEFINE_CONFUI_LOG_FUNC(Fatal, FATAL)
-DEFINE_CONFUI_LOG_FUNC(Error, ERROR)
-DEFINE_CONFUI_LOG_FUNC(Warning, WARNING)
-DEFINE_CONFUI_LOG_FUNC(Debug, DEBUG)
-DEFINE_CONFUI_LOG_FUNC(Info, INFO)
-DEFINE_CONFUI_LOG_FUNC(Verbose, VERBOSE)
-
-template <typename... Args>
-void PassOrDie(bool cond, Args&&... args) {
- CHECK(cond) << ToConLog(std::forward<Args>(args)...);
-}
+#define ConfUiCheck(cond) CHECK(cond) << "ConfUI: "
} // end of namespace confui
} // end of namespace cuttlefish
diff --git a/common/libs/device_config/host_device_config.cpp b/common/libs/device_config/host_device_config.cpp
index 35ec27f..eb28c63 100644
--- a/common/libs/device_config/host_device_config.cpp
+++ b/common/libs/device_config/host_device_config.cpp
@@ -163,8 +163,9 @@
device_display_config->set_width(cuttlefish_display_config.width);
device_display_config->set_height(cuttlefish_display_config.height);
- device_display_config->set_dpi(cuttlefish_config.dpi());
- device_display_config->set_refresh_rate_hz(cuttlefish_config.refresh_rate_hz());
+ device_display_config->set_dpi(cuttlefish_display_config.dpi);
+ device_display_config->set_refresh_rate_hz(
+ cuttlefish_display_config.refresh_rate_hz);
}
}
diff --git a/common/libs/fs/shared_fd.cpp b/common/libs/fs/shared_fd.cpp
index 8b21f0c..d206028 100644
--- a/common/libs/fs/shared_fd.cpp
+++ b/common/libs/fs/shared_fd.cpp
@@ -444,7 +444,7 @@
return rval;
}
-SharedFD SharedFD::VsockServer(unsigned int port, int type) {
+SharedFD SharedFD::VsockServer(unsigned int port, int type, unsigned int cid) {
auto vsock = SharedFD::Socket(AF_VSOCK, type, 0);
if (!vsock->IsOpen()) {
return vsock;
@@ -452,15 +452,17 @@
sockaddr_vm addr{};
addr.svm_family = AF_VSOCK;
addr.svm_port = port;
- addr.svm_cid = VMADDR_CID_ANY;
+ addr.svm_cid = cid;
auto casted_addr = reinterpret_cast<sockaddr*>(&addr);
if (vsock->Bind(casted_addr, sizeof(addr)) == -1) {
- LOG(ERROR) << "Bind failed (" << vsock->StrError() << ")";
+ LOG(ERROR) << "Port " << port << " Bind failed (" << vsock->StrError()
+ << ")";
return SharedFD::ErrorFD(vsock->GetErrno());
}
if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
if (vsock->Listen(4) < 0) {
- LOG(ERROR) << "Listen failed (" << vsock->StrError() << ")";
+ LOG(ERROR) << "Port" << port << " Listen failed (" << vsock->StrError()
+ << ")";
return SharedFD::ErrorFD(vsock->GetErrno());
}
}
@@ -511,4 +513,229 @@
}
}
+/* static */ std::shared_ptr<FileInstance> FileInstance::ClosedInstance() {
+ return std::shared_ptr<FileInstance>(new FileInstance(-1, EBADF));
+}
+
+int FileInstance::Bind(const struct sockaddr* addr, socklen_t addrlen) {
+ errno = 0;
+ int rval = bind(fd_, addr, addrlen);
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::Connect(const struct sockaddr* addr, socklen_t addrlen) {
+ errno = 0;
+ int rval = connect(fd_, addr, addrlen);
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::UNMANAGED_Dup() {
+ errno = 0;
+ int rval = TEMP_FAILURE_RETRY(dup(fd_));
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::UNMANAGED_Dup2(int newfd) {
+ errno = 0;
+ int rval = TEMP_FAILURE_RETRY(dup2(fd_, newfd));
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::Fcntl(int command, int value) {
+ errno = 0;
+ int rval = TEMP_FAILURE_RETRY(fcntl(fd_, command, value));
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::GetSockName(struct sockaddr* addr, socklen_t* addrlen) {
+ errno = 0;
+ int rval = TEMP_FAILURE_RETRY(getsockname(fd_, addr, addrlen));
+ if (rval == -1) {
+ errno_ = errno;
+ }
+ return rval;
+}
+
+unsigned int FileInstance::VsockServerPort() {
+ struct sockaddr_vm vm_socket;
+ socklen_t length = sizeof(vm_socket);
+ GetSockName(reinterpret_cast<struct sockaddr*>(&vm_socket), &length);
+ return vm_socket.svm_port;
+}
+
+int FileInstance::Ioctl(int request, void* val) {
+ errno = 0;
+ int rval = TEMP_FAILURE_RETRY(ioctl(fd_, request, val));
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::LinkAtCwd(const std::string& path) {
+ std::string name = "/proc/self/fd/";
+ name += std::to_string(fd_);
+ errno = 0;
+ int rval =
+ linkat(-1, name.c_str(), AT_FDCWD, path.c_str(), AT_SYMLINK_FOLLOW);
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::Listen(int backlog) {
+ errno = 0;
+ int rval = listen(fd_, backlog);
+ errno_ = errno;
+ return rval;
+}
+
+off_t FileInstance::LSeek(off_t offset, int whence) {
+ errno = 0;
+ off_t rval = TEMP_FAILURE_RETRY(lseek(fd_, offset, whence));
+ errno_ = errno;
+ return rval;
+}
+
+ssize_t FileInstance::Recv(void* buf, size_t len, int flags) {
+ errno = 0;
+ ssize_t rval = TEMP_FAILURE_RETRY(recv(fd_, buf, len, flags));
+ errno_ = errno;
+ return rval;
+}
+
+ssize_t FileInstance::RecvMsg(struct msghdr* msg, int flags) {
+ errno = 0;
+ ssize_t rval = TEMP_FAILURE_RETRY(recvmsg(fd_, msg, flags));
+ errno_ = errno;
+ return rval;
+}
+
+ssize_t FileInstance::Read(void* buf, size_t count) {
+ errno = 0;
+ ssize_t rval = TEMP_FAILURE_RETRY(read(fd_, buf, count));
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::EventfdRead(eventfd_t* value) {
+ errno = 0;
+ auto rval = eventfd_read(fd_, value);
+ errno_ = errno;
+ return rval;
+}
+
+ssize_t FileInstance::Send(const void* buf, size_t len, int flags) {
+ errno = 0;
+ ssize_t rval = TEMP_FAILURE_RETRY(send(fd_, buf, len, flags));
+ errno_ = errno;
+ return rval;
+}
+
+ssize_t FileInstance::SendMsg(const struct msghdr* msg, int flags) {
+ errno = 0;
+ ssize_t rval = TEMP_FAILURE_RETRY(sendmsg(fd_, msg, flags));
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::Shutdown(int how) {
+ errno = 0;
+ int rval = shutdown(fd_, how);
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::SetSockOpt(int level, int optname, const void* optval,
+ socklen_t optlen) {
+ errno = 0;
+ int rval = setsockopt(fd_, level, optname, optval, optlen);
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::GetSockOpt(int level, int optname, void* optval,
+ socklen_t* optlen) {
+ errno = 0;
+ int rval = getsockopt(fd_, level, optname, optval, optlen);
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::SetTerminalRaw() {
+ errno = 0;
+ termios terminal_settings;
+ int rval = tcgetattr(fd_, &terminal_settings);
+ errno_ = errno;
+ if (rval < 0) {
+ return rval;
+ }
+ cfmakeraw(&terminal_settings);
+ rval = tcsetattr(fd_, TCSANOW, &terminal_settings);
+ errno_ = errno;
+ return rval;
+}
+
+std::string FileInstance::StrError() const {
+ errno = 0;
+ return std::string(strerror(errno_));
+}
+
+ScopedMMap FileInstance::MMap(void* addr, size_t length, int prot, int flags,
+ off_t offset) {
+ errno = 0;
+ auto ptr = mmap(addr, length, prot, flags, fd_, offset);
+ errno_ = errno;
+ return ScopedMMap(ptr, length);
+}
+
+ssize_t FileInstance::Truncate(off_t length) {
+ errno = 0;
+ ssize_t rval = TEMP_FAILURE_RETRY(ftruncate(fd_, length));
+ errno_ = errno;
+ return rval;
+}
+
+ssize_t FileInstance::Write(const void* buf, size_t count) {
+ errno = 0;
+ ssize_t rval = TEMP_FAILURE_RETRY(write(fd_, buf, count));
+ errno_ = errno;
+ return rval;
+}
+
+int FileInstance::EventfdWrite(eventfd_t value) {
+ errno = 0;
+ int rval = eventfd_write(fd_, value);
+ errno_ = errno;
+ return rval;
+}
+
+bool FileInstance::IsATTY() {
+ errno = 0;
+ int rval = isatty(fd_);
+ errno_ = errno;
+ return rval;
+}
+
+FileInstance::FileInstance(int fd, int in_errno) : fd_(fd), errno_(in_errno) {
+ // Ensure every file descriptor managed by a FileInstance has the CLOEXEC
+ // flag
+ TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD, FD_CLOEXEC));
+ std::stringstream identity;
+ identity << "fd=" << fd << " @" << this;
+ identity_ = identity.str();
+}
+
+FileInstance* FileInstance::Accept(struct sockaddr* addr,
+ socklen_t* addrlen) const {
+ int fd = TEMP_FAILURE_RETRY(accept(fd_, addr, addrlen));
+ if (fd == -1) {
+ return new FileInstance(fd, errno);
+ } else {
+ return new FileInstance(fd, 0);
+ }
+}
+
} // namespace cuttlefish
diff --git a/common/libs/fs/shared_fd.h b/common/libs/fs/shared_fd.h
index e712ab9..3520a92 100644
--- a/common/libs/fs/shared_fd.h
+++ b/common/libs/fs/shared_fd.h
@@ -141,7 +141,8 @@
static SharedFD SocketLocalServer(const std::string& name, bool is_abstract,
int in_type, mode_t mode);
static SharedFD SocketLocalServer(int port, int type);
- static SharedFD VsockServer(unsigned int port, int type);
+ static SharedFD VsockServer(unsigned int port, int type,
+ unsigned int cid = VMADDR_CID_ANY);
static SharedFD VsockServer(int type);
static SharedFD VsockClient(unsigned int cid, unsigned int port, int type);
@@ -231,27 +232,12 @@
virtual ~FileInstance() { Close(); }
// This can't be a singleton because our shared_ptr's aren't thread safe.
- static std::shared_ptr<FileInstance> ClosedInstance() {
- return std::shared_ptr<FileInstance>(new FileInstance(-1, EBADF));
- }
+ static std::shared_ptr<FileInstance> ClosedInstance();
- int Bind(const struct sockaddr* addr, socklen_t addrlen) {
- errno = 0;
- int rval = bind(fd_, addr, addrlen);
- errno_ = errno;
- return rval;
- }
-
- int Connect(const struct sockaddr* addr, socklen_t addrlen) {
- errno = 0;
- int rval = connect(fd_, addr, addrlen);
- errno_ = errno;
- return rval;
- }
-
+ int Bind(const struct sockaddr* addr, socklen_t addrlen);
+ int Connect(const struct sockaddr* addr, socklen_t addrlen);
int ConnectWithTimeout(const struct sockaddr* addr, socklen_t addrlen,
struct timeval* timeout);
-
void Close();
// Returns true if the entire input was copied.
@@ -260,52 +246,16 @@
// reference type.
bool CopyFrom(FileInstance& in, size_t length);
- int UNMANAGED_Dup() {
- errno = 0;
- int rval = TEMP_FAILURE_RETRY(dup(fd_));
- errno_ = errno;
- return rval;
- }
-
- int UNMANAGED_Dup2(int newfd) {
- errno = 0;
- int rval = TEMP_FAILURE_RETRY(dup2(fd_, newfd));
- errno_ = errno;
- return rval;
- }
-
- int Fcntl(int command, int value) {
- errno = 0;
- int rval = TEMP_FAILURE_RETRY(fcntl(fd_, command, value));
- errno_ = errno;
- return rval;
- }
+ int UNMANAGED_Dup();
+ int UNMANAGED_Dup2(int newfd);
+ int Fcntl(int command, int value);
int GetErrno() const { return errno_; }
+ int GetSockName(struct sockaddr* addr, socklen_t* addrlen);
- int GetSockName(struct sockaddr* addr, socklen_t* addrlen) {
- errno = 0;
- int rval = TEMP_FAILURE_RETRY(getsockname(fd_, addr, addrlen));
- if (rval == -1) {
- errno_ = errno;
- }
- return rval;
- }
+ unsigned int VsockServerPort();
- unsigned int VsockServerPort() {
- struct sockaddr_vm vm_socket;
- socklen_t length = sizeof(vm_socket);
- GetSockName(reinterpret_cast<struct sockaddr*>(&vm_socket), &length);
- return vm_socket.svm_port;
- }
-
- int Ioctl(int request, void* val = nullptr) {
- errno = 0;
- int rval = TEMP_FAILURE_RETRY(ioctl(fd_, request, val));
- errno_ = errno;
- return rval;
- }
-
+ int Ioctl(int request, void* val = nullptr);
bool IsOpen() const { return fd_ != -1; }
// in probably isn't modified, but the API spec doesn't have const.
@@ -320,73 +270,16 @@
* Using this on a file opened with O_TMPFILE can link it into the filesystem.
*/
// Used with O_TMPFILE files to attach them to the filesystem.
- int LinkAtCwd(const std::string& path) {
- std::string name = "/proc/self/fd/";
- name += std::to_string(fd_);
- errno = 0;
- int rval = linkat(
- -1, name.c_str(), AT_FDCWD, path.c_str(), AT_SYMLINK_FOLLOW);
- errno_ = errno;
- return rval;
- }
-
- int Listen(int backlog) {
- errno = 0;
- int rval = listen(fd_, backlog);
- errno_ = errno;
- return rval;
- }
-
+ int LinkAtCwd(const std::string& path);
+ int Listen(int backlog);
static void Log(const char* message);
-
- off_t LSeek(off_t offset, int whence) {
- errno = 0;
- off_t rval = TEMP_FAILURE_RETRY(lseek(fd_, offset, whence));
- errno_ = errno;
- return rval;
- }
-
- ssize_t Recv(void* buf, size_t len, int flags) {
- errno = 0;
- ssize_t rval = TEMP_FAILURE_RETRY(recv(fd_, buf, len, flags));
- errno_ = errno;
- return rval;
- }
-
- ssize_t RecvMsg(struct msghdr* msg, int flags) {
- errno = 0;
- ssize_t rval = TEMP_FAILURE_RETRY(recvmsg(fd_, msg, flags));
- errno_ = errno;
- return rval;
- }
-
- ssize_t Read(void* buf, size_t count) {
- errno = 0;
- ssize_t rval = TEMP_FAILURE_RETRY(read(fd_, buf, count));
- errno_ = errno;
- return rval;
- }
-
- int EventfdRead(eventfd_t* value) {
- errno = 0;
- auto rval = eventfd_read(fd_, value);
- errno_ = errno;
- return rval;
- }
-
- ssize_t Send(const void* buf, size_t len, int flags) {
- errno = 0;
- ssize_t rval = TEMP_FAILURE_RETRY(send(fd_, buf, len, flags));
- errno_ = errno;
- return rval;
- }
-
- ssize_t SendMsg(const struct msghdr* msg, int flags) {
- errno = 0;
- ssize_t rval = TEMP_FAILURE_RETRY(sendmsg(fd_, msg, flags));
- errno_ = errno;
- return rval;
- }
+ off_t LSeek(off_t offset, int whence);
+ ssize_t Recv(void* buf, size_t len, int flags);
+ ssize_t RecvMsg(struct msghdr* msg, int flags);
+ ssize_t Read(void* buf, size_t count);
+ int EventfdRead(eventfd_t* value);
+ ssize_t Send(const void* buf, size_t len, int flags);
+ ssize_t SendMsg(const struct msghdr* msg, int flags);
template <typename... Args>
ssize_t SendFileDescriptors(const void* buf, size_t len, Args&&... sent_fds) {
@@ -398,118 +291,25 @@
return ret;
}
- int Shutdown(int how) {
- errno = 0;
- int rval = shutdown(fd_, how);
- errno_ = errno;
- return rval;
- }
-
+ int Shutdown(int how);
void Set(fd_set* dest, int* max_index) const;
-
- int SetSockOpt(int level, int optname, const void* optval, socklen_t optlen) {
- errno = 0;
- int rval = setsockopt(fd_, level, optname, optval, optlen);
- errno_ = errno;
- return rval;
- }
-
- int GetSockOpt(int level, int optname, void* optval, socklen_t* optlen) {
- errno = 0;
- int rval = getsockopt(fd_, level, optname, optval, optlen);
- errno_ = errno;
- return rval;
- }
-
- int SetTerminalRaw() {
- errno = 0;
- termios terminal_settings;
- int rval = tcgetattr(fd_, &terminal_settings);
- errno_ = errno;
- if (rval < 0) {
- return rval;
- }
- cfmakeraw(&terminal_settings);
- rval = tcsetattr(fd_, TCSANOW, &terminal_settings);
- errno_ = errno;
- return rval;
- }
-
- const char* StrError() const {
- errno = 0;
- FileInstance* s = const_cast<FileInstance*>(this);
- char* out = strerror_r(errno_, s->strerror_buf_, sizeof(strerror_buf_));
-
- // From man page:
- // strerror_r() returns a pointer to a string containing the error message.
- // This may be either a pointer to a string that the function stores in
- // buf, or a pointer to some (immutable) static string (in which case buf
- // is unused).
- if (out != s->strerror_buf_) {
- strncpy(s->strerror_buf_, out, sizeof(strerror_buf_));
- }
- return strerror_buf_;
- }
-
- ScopedMMap MMap(void* addr, size_t length, int prot, int flags,
- off_t offset) {
- errno = 0;
- auto ptr = mmap(addr, length, prot, flags, fd_, offset);
- errno_ = errno;
- return ScopedMMap(ptr, length);
- }
-
- ssize_t Truncate(off_t length) {
- errno = 0;
- ssize_t rval = TEMP_FAILURE_RETRY(ftruncate(fd_, length));
- errno_ = errno;
- return rval;
- }
-
- ssize_t Write(const void* buf, size_t count) {
- errno = 0;
- ssize_t rval = TEMP_FAILURE_RETRY(write(fd_, buf, count));
- errno_ = errno;
- return rval;
- }
-
- int EventfdWrite(eventfd_t value) {
- errno = 0;
- int rval = eventfd_write(fd_, value);
- errno_ = errno;
- return rval;
- }
-
- bool IsATTY() {
- errno = 0;
- int rval = isatty(fd_);
- errno_ = errno;
- return rval;
- }
+ int SetSockOpt(int level, int optname, const void* optval, socklen_t optlen);
+ int GetSockOpt(int level, int optname, void* optval, socklen_t* optlen);
+ int SetTerminalRaw();
+ std::string StrError() const;
+ ScopedMMap MMap(void* addr, size_t length, int prot, int flags, off_t offset);
+ ssize_t Truncate(off_t length);
+ ssize_t Write(const void* buf, size_t count);
+ int EventfdWrite(eventfd_t value);
+ bool IsATTY();
private:
- FileInstance(int fd, int in_errno) : fd_(fd), errno_(in_errno) {
- // Ensure every file descriptor managed by a FileInstance has the CLOEXEC
- // flag
- TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD, FD_CLOEXEC));
- std::stringstream identity;
- identity << "fd=" << fd << " @" << this;
- identity_ = identity.str();
- }
-
- FileInstance* Accept(struct sockaddr* addr, socklen_t* addrlen) const {
- int fd = TEMP_FAILURE_RETRY(accept(fd_, addr, addrlen));
- if (fd == -1) {
- return new FileInstance(fd, errno);
- } else {
- return new FileInstance(fd, 0);
- }
- }
+ FileInstance(int fd, int in_errno);
+ FileInstance* Accept(struct sockaddr* addr, socklen_t* addrlen) const;
int fd_;
int errno_;
std::string identity_;
- char strerror_buf_[160];
};
/* Methods that need both a fully defined SharedFD and a fully defined
diff --git a/common/libs/time/monotonic_time.h b/common/libs/time/monotonic_time.h
deleted file mode 100644
index 2839001..0000000
--- a/common/libs/time/monotonic_time.h
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <stdint.h>
-#include <time.h>
-
-namespace cuttlefish {
-namespace time {
-
-static const int64_t kNanosecondsPerSecond = 1000000000;
-
-class TimeDifference {
- public:
- TimeDifference(time_t seconds, long nanoseconds, int64_t scale) :
- scale_(scale), truncated_(false) {
- ts_.tv_sec = seconds;
- ts_.tv_nsec = nanoseconds;
- if (scale_ == kNanosecondsPerSecond) {
- truncated_ = true;
- truncated_ns_ = 0;
- }
- }
-
- TimeDifference(const TimeDifference& in, int64_t scale) :
- scale_(scale), truncated_(false) {
- ts_ = in.GetTS();
- if (scale_ == kNanosecondsPerSecond) {
- truncated_ = true;
- truncated_ns_ = 0;
- } else if ((in.scale_ % scale_) == 0) {
- truncated_ = true;
- truncated_ns_ = ts_.tv_nsec;
- }
- }
-
- TimeDifference(const struct timespec& in, int64_t scale) :
- ts_(in), scale_(scale), truncated_(false) { }
-
- TimeDifference operator*(const uint32_t factor) {
- TimeDifference rval = *this;
- rval.ts_.tv_sec = ts_.tv_sec * factor;
- // Create temporary variable to hold the multiplied
- // nanoseconds so that no overflow is possible.
- // Nanoseconds must be in [0, 10^9) and so all are less
- // then 2^30. Even multiplied by the largest uint32
- // this will fit in a 64-bit int without overflow.
- int64_t tv_nsec = static_cast<int64_t>(ts_.tv_nsec) * factor;
- rval.ts_.tv_sec += (tv_nsec / kNanosecondsPerSecond);
- rval.ts_.tv_nsec = tv_nsec % kNanosecondsPerSecond;
- return rval;
- }
-
- TimeDifference operator+(const TimeDifference& other) const {
- struct timespec ret = ts_;
- ret.tv_nsec = (ts_.tv_nsec + other.ts_.tv_nsec) % 1000000000;
- ret.tv_sec = (ts_.tv_sec + other.ts_.tv_sec) +
- (ts_.tv_nsec + other.ts_.tv_nsec) / 1000000000;
- return TimeDifference(ret, scale_ < other.scale_ ? scale_: other.scale_);
- }
-
- TimeDifference operator-(const TimeDifference& other) const {
- struct timespec ret = ts_;
- // Keeps nanoseconds positive and allow negative numbers only on
- // seconds.
- ret.tv_nsec = (1000000000 + ts_.tv_nsec - other.ts_.tv_nsec) % 1000000000;
- ret.tv_sec = (ts_.tv_sec - other.ts_.tv_sec) -
- (ts_.tv_nsec < other.ts_.tv_nsec ? 1 : 0);
- return TimeDifference(ret, scale_ < other.scale_ ? scale_: other.scale_);
- }
-
- bool operator<(const TimeDifference& other) const {
- return ts_.tv_sec < other.ts_.tv_sec ||
- (ts_.tv_sec == other.ts_.tv_sec && ts_.tv_nsec < other.ts_.tv_nsec);
- }
-
- int64_t count() const {
- return ts_.tv_sec * (kNanosecondsPerSecond / scale_) + ts_.tv_nsec / scale_;
- }
-
- time_t seconds() const {
- return ts_.tv_sec;
- }
-
- long subseconds_in_ns() const {
- if (!truncated_) {
- truncated_ns_ = (ts_.tv_nsec / scale_) * scale_;
- truncated_ = true;
- }
- return truncated_ns_;
- }
-
- struct timespec GetTS() const {
- // We can't assume C++11, so avoid extended initializer lists.
- struct timespec rval = { ts_.tv_sec, subseconds_in_ns()};
- return rval;
- }
-
- protected:
- struct timespec ts_;
- int64_t scale_;
- mutable bool truncated_;
- mutable long truncated_ns_;
-};
-
-class MonotonicTimePoint {
- public:
- static MonotonicTimePoint Now() {
- struct timespec ts;
-#ifdef CLOCK_MONOTONIC_RAW
- // WARNING:
- // While we do have CLOCK_MONOTONIC_RAW, we can't depend on it until:
- // - ALL places relying on MonotonicTimePoint are fixed,
- // - pthread supports pthread_timewait_monotonic.
- //
- // This is currently observable as a LEGITIMATE problem while running
- // pthread_test. DO NOT revert this to CLOCK_MONOTONIC_RAW until test
- // passes.
- clock_gettime(CLOCK_MONOTONIC, &ts);
-#else
- clock_gettime(CLOCK_MONOTONIC, &ts);
-#endif
- return MonotonicTimePoint(ts);
- }
-
- MonotonicTimePoint() {
- ts_.tv_sec = 0;
- ts_.tv_nsec = 0;
- }
-
- explicit MonotonicTimePoint(const struct timespec& ts) {
- ts_ = ts;
- }
-
- TimeDifference SinceEpoch() const {
- return TimeDifference(ts_, 1);
- }
-
- TimeDifference operator-(const MonotonicTimePoint& other) const {
- struct timespec rval;
- rval.tv_sec = ts_.tv_sec - other.ts_.tv_sec;
- rval.tv_nsec = ts_.tv_nsec - other.ts_.tv_nsec;
- if (rval.tv_nsec < 0) {
- --rval.tv_sec;
- rval.tv_nsec += kNanosecondsPerSecond;
- }
- return TimeDifference(rval, 1);
- }
-
- MonotonicTimePoint operator+(const TimeDifference& other) const {
- MonotonicTimePoint rval = *this;
- rval.ts_.tv_sec += other.seconds();
- rval.ts_.tv_nsec += other.subseconds_in_ns();
- if (rval.ts_.tv_nsec >= kNanosecondsPerSecond) {
- ++rval.ts_.tv_sec;
- rval.ts_.tv_nsec -= kNanosecondsPerSecond;
- }
- return rval;
- }
-
- bool operator==(const MonotonicTimePoint& other) const {
- return (ts_.tv_sec == other.ts_.tv_sec) &&
- (ts_.tv_nsec == other.ts_.tv_nsec);
- }
-
- bool operator!=(const MonotonicTimePoint& other) const {
- return !(*this == other);
- }
-
- bool operator<(const MonotonicTimePoint& other) const {
- return ((ts_.tv_sec - other.ts_.tv_sec) < 0) ||
- ((ts_.tv_sec == other.ts_.tv_sec) &&
- (ts_.tv_nsec < other.ts_.tv_nsec));
- }
-
- bool operator>(const MonotonicTimePoint& other) const {
- return other < *this;
- }
-
- bool operator<=(const MonotonicTimePoint& other) const {
- return !(*this > other);
- }
-
- bool operator>=(const MonotonicTimePoint& other) const {
- return !(*this < other);
- }
-
- MonotonicTimePoint& operator+=(const TimeDifference& other) {
- ts_.tv_sec += other.seconds();
- ts_.tv_nsec += other.subseconds_in_ns();
- if (ts_.tv_nsec >= kNanosecondsPerSecond) {
- ++ts_.tv_sec;
- ts_.tv_nsec -= kNanosecondsPerSecond;
- }
- return *this;
- }
-
- MonotonicTimePoint& operator-=(const TimeDifference& other) {
- ts_.tv_sec -= other.seconds();
- ts_.tv_nsec -= other.subseconds_in_ns();
- if (ts_.tv_nsec < 0) {
- --ts_.tv_sec;
- ts_.tv_nsec += kNanosecondsPerSecond;
- }
- return *this;
- }
-
- void ToTimespec(struct timespec* dest) const {
- *dest = ts_;
- }
-
- protected:
- struct timespec ts_;
-};
-
-class Seconds : public TimeDifference {
- public:
- explicit Seconds(const TimeDifference& difference) :
- TimeDifference(difference, kNanosecondsPerSecond) { }
-
- Seconds(int64_t seconds) :
- TimeDifference(seconds, 0, kNanosecondsPerSecond) { }
-};
-
-class Milliseconds : public TimeDifference {
- public:
- explicit Milliseconds(const TimeDifference& difference) :
- TimeDifference(difference, kScale) { }
-
- Milliseconds(int64_t ms) : TimeDifference(
- ms / 1000, (ms % 1000) * kScale, kScale) { }
-
- protected:
- static const int kScale = kNanosecondsPerSecond / 1000;
-};
-
-class Microseconds : public TimeDifference {
- public:
- explicit Microseconds(const TimeDifference& difference) :
- TimeDifference(difference, kScale) { }
-
- Microseconds(int64_t micros) : TimeDifference(
- micros / 1000000, (micros % 1000000) * kScale, kScale) { }
-
- protected:
- static const int kScale = kNanosecondsPerSecond / 1000000;
-};
-
-class Nanoseconds : public TimeDifference {
- public:
- explicit Nanoseconds(const TimeDifference& difference) :
- TimeDifference(difference, 1) { }
- Nanoseconds(int64_t ns) : TimeDifference(ns / kNanosecondsPerSecond,
- ns % kNanosecondsPerSecond, 1) { }
-};
-
-} // namespace time
-} // namespace cuttlefish
-
-/**
- * Legacy support for microseconds. Use MonotonicTimePoint in new code.
- */
-static const int64_t kSecsToUsecs = static_cast<int64_t>(1000) * 1000;
-
-static inline int64_t get_monotonic_usecs() {
- return cuttlefish::time::Microseconds(
- cuttlefish::time::MonotonicTimePoint::Now().SinceEpoch()).count();
-}
diff --git a/common/libs/utils/Android.bp b/common/libs/utils/Android.bp
index f8ca5e2..a64bb18 100644
--- a/common/libs/utils/Android.bp
+++ b/common/libs/utils/Android.bp
@@ -23,25 +23,29 @@
"archive.cpp",
"subprocess.cpp",
"environment.cpp",
- "size_utils.cpp",
+ "flag_parser.cpp",
+ "shared_fd_flag.cpp",
"files.cpp",
"users.cpp",
"network.cpp",
"base64.cpp",
"tcp_socket.cpp",
"tee_logging.cpp",
+ "vsock_connection.cpp",
],
shared: {
shared_libs: [
"libbase",
"libcuttlefish_fs",
"libcrypto",
+ "libjsoncpp",
],
},
static: {
static_libs: [
"libbase",
"libcuttlefish_fs",
+ "libjsoncpp",
],
shared_libs: [
"libcrypto", // libcrypto_static is not accessible from all targets
@@ -49,3 +53,33 @@
},
defaults: ["cuttlefish_host"],
}
+
+cc_test_host {
+ name: "libcuttlefish_utils_test",
+ srcs: [
+ "flag_parser_test.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcuttlefish_fs",
+ "libcuttlefish_utils",
+ ],
+ shared_libs: [
+ "libcrypto",
+ "liblog",
+ "libxml2",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+ defaults: ["cuttlefish_host"],
+}
+
+cc_library {
+ name: "libvsock_utils",
+ srcs: ["vsock_connection.cpp"],
+ shared_libs: ["libbase", "libcuttlefish_fs", "liblog", "libjsoncpp"],
+ defaults: ["cuttlefish_guest_only"],
+ include_dirs: ["device/google/cuttlefish"],
+ export_include_dirs: ["."],
+}
diff --git a/common/libs/utils/flag_parser.cpp b/common/libs/utils/flag_parser.cpp
new file mode 100644
index 0000000..e011de6
--- /dev/null
+++ b/common/libs/utils/flag_parser.cpp
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2021 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/flag_parser.h>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+namespace cuttlefish {
+
+std::ostream& operator<<(std::ostream& out, const FlagAlias& alias) {
+ switch (alias.mode) {
+ case FlagAliasMode::kFlagExact:
+ return out << alias.name;
+ case FlagAliasMode::kFlagPrefix:
+ return out << alias.name << "*";
+ case FlagAliasMode::kFlagConsumesFollowing:
+ return out << alias.name << " *";
+ default:
+ LOG(FATAL) << "Unexpected flag alias mode " << (int)alias.mode;
+ }
+ return out;
+}
+
+Flag& Flag::UnvalidatedAlias(const FlagAlias& alias) & {
+ aliases_.push_back(alias);
+ return *this;
+}
+Flag Flag::UnvalidatedAlias(const FlagAlias& alias) && {
+ aliases_.push_back(alias);
+ return *this;
+}
+
+void Flag::ValidateAlias(const FlagAlias& alias) {
+ using android::base::EndsWith;
+ using android::base::StartsWith;
+
+ CHECK(StartsWith(alias.name, "-")) << "Flags should start with \"-\"";
+ if (alias.mode == FlagAliasMode::kFlagPrefix) {
+ CHECK(EndsWith(alias.name, "=")) << "Prefix flags shold end with \"=\"";
+ }
+
+ CHECK(!HasAlias(alias)) << "Duplicate flag alias: " << alias.name;
+ if (alias.mode == FlagAliasMode::kFlagConsumesFollowing) {
+ CHECK(!HasAlias({FlagAliasMode::kFlagExact, alias.name}))
+ << "Overlapping flag aliases for " << alias.name;
+ } else if (alias.mode == FlagAliasMode::kFlagExact) {
+ CHECK(!HasAlias({FlagAliasMode::kFlagExact, alias.name}))
+ << "Overlapping flag aliases for " << alias.name;
+ }
+}
+
+Flag& Flag::Alias(const FlagAlias& alias) & {
+ ValidateAlias(alias);
+ aliases_.push_back(alias);
+ return *this;
+}
+Flag Flag::Alias(const FlagAlias& alias) && {
+ ValidateAlias(alias);
+ aliases_.push_back(alias);
+ return *this;
+}
+
+Flag& Flag::Help(const std::string& help) & {
+ help_ = help;
+ return *this;
+}
+Flag Flag::Help(const std::string& help) && {
+ help_ = help;
+ return *this;
+}
+
+Flag& Flag::Getter(std::function<std::string()> fn) & {
+ getter_ = std::move(fn);
+ return *this;
+}
+Flag Flag::Getter(std::function<std::string()> fn) && {
+ getter_ = std::move(fn);
+ return *this;
+}
+
+Flag& Flag::Setter(std::function<bool(const FlagMatch&)> fn) & {
+ setter_ = std::move(fn);
+ return *this;
+}
+Flag Flag::Setter(std::function<bool(const FlagMatch&)> fn) && {
+ setter_ = std::move(fn);
+ return *this;
+}
+
+Flag::FlagProcessResult Flag::Process(
+ const std::string& arg, const std::optional<std::string>& next_arg) const {
+ if (!setter_ && aliases_.size() > 0) {
+ LOG(ERROR) << "No setter for flag with alias " << aliases_[0].name;
+ return FlagProcessResult::kFlagError;
+ }
+ for (auto& alias : aliases_) {
+ switch (alias.mode) {
+ case FlagAliasMode::kFlagConsumesFollowing:
+ if (arg != alias.name) {
+ continue;
+ }
+ if (!next_arg) {
+ LOG(ERROR) << "Expected an argument after \"" << arg << "\"";
+ return FlagProcessResult::kFlagError;
+ }
+ if (!(*setter_)({arg, *next_arg})) {
+ LOG(ERROR) << "Processing \"" << arg << "\" \"" << *next_arg
+ << "\" failed";
+ return FlagProcessResult::kFlagError;
+ }
+ return FlagProcessResult::kFlagConsumedWithFollowing;
+ case FlagAliasMode::kFlagExact:
+ if (arg != alias.name) {
+ continue;
+ }
+ if (!(*setter_)({arg, arg})) {
+ LOG(ERROR) << "Processing \"" << arg << "\" failed";
+ return FlagProcessResult::kFlagError;
+ }
+ return FlagProcessResult::kFlagConsumed;
+ case FlagAliasMode::kFlagPrefix:
+ if (!android::base::StartsWith(arg, alias.name)) {
+ continue;
+ }
+ if (!(*setter_)({alias.name, arg.substr(alias.name.size())})) {
+ LOG(ERROR) << "Processing \"" << arg << "\" failed";
+ return FlagProcessResult::kFlagError;
+ }
+ return FlagProcessResult::kFlagConsumed;
+ default:
+ LOG(ERROR) << "Unknown flag alias mode: " << (int)alias.mode;
+ return FlagProcessResult::kFlagError;
+ }
+ }
+ return FlagProcessResult::kFlagSkip;
+}
+
+bool Flag::Parse(std::vector<std::string>& arguments) const {
+ for (int i = 0; i < arguments.size();) {
+ std::string arg = arguments[i];
+ std::optional<std::string> next_arg;
+ if (i < arguments.size() - 1) {
+ next_arg = arguments[i + 1];
+ }
+ auto result = Process(arg, next_arg);
+ if (result == FlagProcessResult::kFlagError) {
+ LOG(ERROR) << "Failure in parsing \"" << arg << "\"";
+ return false;
+ } else if (result == FlagProcessResult::kFlagConsumed) {
+ arguments.erase(arguments.begin() + i);
+ } else if (result == FlagProcessResult::kFlagConsumedWithFollowing) {
+ arguments.erase(arguments.begin() + i, arguments.begin() + i + 2);
+ } else if (result == FlagProcessResult::kFlagSkip) {
+ i++;
+ continue;
+ } else {
+ LOG(ERROR) << "Unknown FlagProcessResult: " << (int)result;
+ return false;
+ }
+ }
+ return true;
+}
+bool Flag::Parse(std::vector<std::string>&& arguments) const {
+ return Parse(static_cast<std::vector<std::string>&>(arguments));
+}
+
+bool Flag::HasAlias(const FlagAlias& test) const {
+ for (const auto& alias : aliases_) {
+ if (alias.mode == test.mode && alias.name == test.name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static std::string XmlEscape(const std::string& s) {
+ using android::base::StringReplace;
+ return StringReplace(StringReplace(s, "<", "<", true), ">", ">", true);
+}
+
+bool Flag::WriteGflagsCompatXml(std::ostream& out) const {
+ std::unordered_set<std::string> name_guesses;
+ for (const auto& alias : aliases_) {
+ std::string_view name = alias.name;
+ if (!android::base::ConsumePrefix(&name, "-")) {
+ continue;
+ }
+ android::base::ConsumePrefix(&name, "-");
+ if (alias.mode == FlagAliasMode::kFlagExact) {
+ android::base::ConsumePrefix(&name, "no");
+ name_guesses.insert(std::string{name});
+ } else if (alias.mode == FlagAliasMode::kFlagConsumesFollowing) {
+ name_guesses.insert(std::string{name});
+ } else if (alias.mode == FlagAliasMode::kFlagPrefix) {
+ if (!android::base::ConsumeSuffix(&name, "=")) {
+ continue;
+ }
+ name_guesses.insert(std::string{name});
+ }
+ }
+ bool found_alias = false;
+ for (const auto& name : name_guesses) {
+ bool has_bool_aliases =
+ HasAlias({FlagAliasMode::kFlagPrefix, "-" + name + "="}) &&
+ HasAlias({FlagAliasMode::kFlagPrefix, "--" + name + "="}) &&
+ HasAlias({FlagAliasMode::kFlagExact, "-" + name}) &&
+ HasAlias({FlagAliasMode::kFlagExact, "--" + name}) &&
+ HasAlias({FlagAliasMode::kFlagExact, "-no" + name}) &&
+ HasAlias({FlagAliasMode::kFlagExact, "--no" + name});
+ bool has_other_aliases =
+ HasAlias({FlagAliasMode::kFlagPrefix, "-" + name + "="}) &&
+ HasAlias({FlagAliasMode::kFlagPrefix, "--" + name + "="}) &&
+ HasAlias({FlagAliasMode::kFlagConsumesFollowing, "-" + name}) &&
+ HasAlias({FlagAliasMode::kFlagConsumesFollowing, "--" + name});
+ if (has_bool_aliases && has_other_aliases) {
+ LOG(ERROR) << "Expected exactly one of has_bool_aliases and "
+ << "has_other_aliases, got both for \"" << name << "\".";
+ return false;
+ } else if (!has_bool_aliases && !has_other_aliases) {
+ continue;
+ }
+ found_alias = true;
+ // Lifted from external/gflags/src/gflags_reporting.cc:DescribeOneFlagInXML
+ out << "<flag>\n";
+ out << " <file>file.cc</file>\n";
+ out << " <name>" << XmlEscape(name) << "</name>\n";
+ auto help = help_ ? XmlEscape(*help_) : std::string{""};
+ out << " <meaning>" << help << "</meaning>\n";
+ auto value = getter_ ? XmlEscape((*getter_)()) : std::string{""};
+ out << " <default>" << value << "</default>\n";
+ out << " <current>" << value << "</current>\n";
+ out << " <type>" << (has_bool_aliases ? "bool" : "string") << "</type>\n";
+ out << "</flag>\n";
+ }
+ return found_alias;
+}
+
+std::ostream& operator<<(std::ostream& out, const Flag& flag) {
+ out << "[";
+ for (auto it = flag.aliases_.begin(); it != flag.aliases_.end(); it++) {
+ if (it != flag.aliases_.begin()) {
+ out << ", ";
+ }
+ out << *it;
+ }
+ out << "]\n";
+ if (flag.help_) {
+ out << "(" << *flag.help_ << ")\n";
+ }
+ if (flag.getter_) {
+ out << "(Current value: \"" << (*flag.getter_)() << "\")\n";
+ }
+ return out;
+}
+
+std::vector<std::string> ArgsToVec(int argc, char** argv) {
+ std::vector<std::string> args;
+ for (int i = 0; i < argc; i++) {
+ args.push_back(argv[i]);
+ }
+ return args;
+}
+
+bool ParseFlags(const std::vector<Flag>& flags,
+ std::vector<std::string>& args) {
+ for (const auto& flag : flags) {
+ if (!flag.Parse(args)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ParseFlags(const std::vector<Flag>& flags,
+ std::vector<std::string>&& args) {
+ for (const auto& flag : flags) {
+ if (!flag.Parse(args)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool WriteGflagsCompatXml(const std::vector<Flag>& flags, std::ostream& out) {
+ for (const auto& flag : flags) {
+ if (!flag.WriteGflagsCompatXml(out)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+Flag HelpFlag(const std::vector<Flag>& flags, const std::string& text) {
+ auto setter = [&](FlagMatch) {
+ if (text.size() > 0) {
+ LOG(INFO) << text;
+ }
+ for (const auto& flag : flags) {
+ LOG(INFO) << flag;
+ }
+ return false;
+ };
+ return Flag()
+ .Alias({FlagAliasMode::kFlagExact, "-help"})
+ .Alias({FlagAliasMode::kFlagExact, "--help"})
+ .Setter(setter);
+}
+
+Flag InvalidFlagGuard() {
+ return Flag()
+ .UnvalidatedAlias({FlagAliasMode::kFlagPrefix, "-"})
+ .Help(
+ "This executable only supports the flags in `-help`. Positional "
+ "arguments may be supported.")
+ .Setter([](const FlagMatch& match) {
+ LOG(ERROR) << "Unknown flag " << match.value;
+ return false;
+ });
+}
+
+Flag UnexpectedArgumentGuard() {
+ return Flag()
+ .UnvalidatedAlias({FlagAliasMode::kFlagPrefix, ""})
+ .Help(
+ "This executable only supports the flags in `-help`. Positional "
+ "arguments are not supported.")
+ .Setter([](const FlagMatch& match) {
+ LOG(ERROR) << "Unexpected argument \"" << match.value << "\"";
+ return false;
+ });
+}
+
+Flag GflagsCompatFlag(const std::string& name) {
+ return Flag()
+ .Alias({FlagAliasMode::kFlagPrefix, "-" + name + "="})
+ .Alias({FlagAliasMode::kFlagPrefix, "--" + name + "="})
+ .Alias({FlagAliasMode::kFlagConsumesFollowing, "-" + name})
+ .Alias({FlagAliasMode::kFlagConsumesFollowing, "--" + name});
+};
+
+Flag GflagsCompatFlag(const std::string& name, std::string& value) {
+ return GflagsCompatFlag(name)
+ .Getter([&value]() { return value; })
+ .Setter([&value](const FlagMatch& match) {
+ value = match.value;
+ return true;
+ });
+}
+
+template <typename T>
+std::optional<T> ParseInteger(const std::string& value) {
+ if (value.size() == 0) {
+ return {};
+ }
+ const char* base = value.c_str();
+ char* end = nullptr;
+ errno = 0;
+ auto r = strtoll(base, &end, /* auto-detect */ 0);
+ if (errno != 0 || end != base + value.size()) {
+ return {};
+ }
+ if (static_cast<T>(r) != r) {
+ return {};
+ }
+ return r;
+}
+
+template <typename T>
+static Flag GflagsCompatNumericFlagGeneric(const std::string& name, T& value) {
+ return GflagsCompatFlag(name)
+ .Getter([&value]() { return std::to_string(value); })
+ .Setter([&value](const FlagMatch& match) {
+ auto parsed = ParseInteger<T>(match.value);
+ if (parsed) {
+ value = *parsed;
+ return true;
+ } else {
+ LOG(ERROR) << "Failed to parse \"" << match.value
+ << "\" as an integer";
+ return false;
+ }
+ });
+}
+
+Flag GflagsCompatFlag(const std::string& name, int32_t& value) {
+ return GflagsCompatNumericFlagGeneric(name, value);
+}
+
+Flag GflagsCompatFlag(const std::string& name, bool& value) {
+ return Flag()
+ .Alias({FlagAliasMode::kFlagPrefix, "-" + name + "="})
+ .Alias({FlagAliasMode::kFlagPrefix, "--" + name + "="})
+ .Alias({FlagAliasMode::kFlagExact, "-" + name})
+ .Alias({FlagAliasMode::kFlagExact, "--" + name})
+ .Alias({FlagAliasMode::kFlagExact, "-no" + name})
+ .Alias({FlagAliasMode::kFlagExact, "--no" + name})
+ .Getter([&value]() { return value ? "true" : "false"; })
+ .Setter([name, &value](const FlagMatch& match) {
+ const auto& key = match.key;
+ if (key == "-" + name || key == "--" + name) {
+ value = true;
+ return true;
+ } else if (key == "-no" + name || key == "--no" + name) {
+ value = false;
+ return true;
+ } else if (key == "-" + name + "=" || key == "--" + name + "=") {
+ if (match.value == "true") {
+ value = true;
+ return true;
+ } else if (match.value == "false") {
+ value = false;
+ return true;
+ } else {
+ LOG(ERROR) << "Unexpected boolean value \"" << match.value << "\""
+ << " for \"" << name << "\"";
+ return false;
+ }
+ }
+ LOG(ERROR) << "Unexpected key \"" << match.key << "\""
+ << " for \"" << name << "\"";
+ return false;
+ });
+};
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/flag_parser.h b/common/libs/utils/flag_parser.h
new file mode 100644
index 0000000..9f66ded
--- /dev/null
+++ b/common/libs/utils/flag_parser.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2021 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 <functional>
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+
+/* Support for parsing individual flags out of a larger list of flags. This
+ * supports externally determining the order that flags are evaluated in, and
+ * incrementally integrating with existing flag parsing implementations.
+ *
+ * Start with Flag() or one of the GflagsCompatFlag(...) functions to create new
+ * flags. These flags should be aggregated through the application through some
+ * other mechanism and then evaluated individually with Flag::Parse or together
+ * with ParseFlags on arguments. */
+
+namespace cuttlefish {
+
+/* The matching behavior used with the name in `FlagAlias::name`. */
+enum class FlagAliasMode {
+ /* Match arguments of the form `<name><value>`. In practice, <name> usually
+ * looks like "-flag=" or "--flag=", where the "-" and "=" are included in
+ * parsing. */
+ kFlagPrefix,
+ /* Match arguments of the form `<name>`. In practice, <name> will look like
+ * "-flag" or "--flag". */
+ kFlagExact,
+ /* Match a pair of arguments of the form `<name>` `<value>`. In practice,
+ * <name> will look like "-flag" or "--flag". */
+ kFlagConsumesFollowing,
+};
+
+/* A single matching rule for a `Flag`. One `Flag` can have multiple rules. */
+struct FlagAlias {
+ FlagAliasMode mode;
+ std::string name;
+};
+
+std::ostream& operator<<(std::ostream&, const FlagAlias&);
+
+/* A successful match in an argument list from a `FlagAlias` inside a `Flag`.
+ * The `key` value corresponds to `FlagAlias::name`. For a match of
+ * `FlagAliasMode::kFlagExact`, `key` and `value` will both be the `name`. */
+struct FlagMatch {
+ std::string key;
+ std::string value;
+};
+
+class Flag {
+ public:
+ /* Add an alias that triggers matches and calls to the `Setter` function. */
+ Flag& Alias(const FlagAlias& alias) &;
+ Flag Alias(const FlagAlias& alias) &&;
+ /* Set help text, visible in the class ostream writer method. Optional. */
+ Flag& Help(const std::string&) &;
+ Flag Help(const std::string&) &&;
+ /* Set a loader that displays the current value in help text. Optional. */
+ Flag& Getter(std::function<std::string()>) &;
+ Flag Getter(std::function<std::string()>) &&;
+ /* Set the callback for matches. The callback be invoked multiple times. */
+ Flag& Setter(std::function<bool(const FlagMatch&)>) &;
+ Flag Setter(std::function<bool(const FlagMatch&)>) &&;
+
+ /* Examines a list of arguments, removing any matches from the list and
+ * invoking the `Setter` for every match. Returns `false` if the callback ever
+ * returns `false`. Non-matches are left in place. */
+ bool Parse(std::vector<std::string>& flags) const;
+ bool Parse(std::vector<std::string>&& flags) const;
+
+ /* Write gflags `--helpxml` style output for a string-type flag. */
+ bool WriteGflagsCompatXml(std::ostream&) const;
+
+ private:
+ /* Reports whether `Process` wants to consume zero, one, or two arguments. */
+ enum class FlagProcessResult {
+ /* Error in handling a flag, exit flag handling with an error result. */
+ kFlagError,
+ kFlagSkip, /* Flag skipped; consume no arguments. */
+ kFlagConsumed, /* Flag processed; consume one argument. */
+ kFlagConsumedWithFollowing, /* Flag processed; consume 2 arguments. */
+ };
+
+ void ValidateAlias(const FlagAlias& alias);
+ Flag& UnvalidatedAlias(const FlagAlias& alias) &;
+ Flag UnvalidatedAlias(const FlagAlias& alias) &&;
+
+ /* Attempt to match a single argument. */
+ FlagProcessResult Process(const std::string& argument,
+ const std::optional<std::string>& next_arg) const;
+
+ bool HasAlias(const FlagAlias&) const;
+
+ friend std::ostream& operator<<(std::ostream&, const Flag&);
+ friend Flag InvalidFlagGuard();
+ friend Flag UnexpectedArgumentGuard();
+
+ std::vector<FlagAlias> aliases_;
+ std::optional<std::string> help_;
+ std::optional<std::function<std::string()>> getter_;
+ std::optional<std::function<bool(const FlagMatch&)>> setter_;
+};
+
+std::ostream& operator<<(std::ostream&, const Flag&);
+
+std::vector<std::string> ArgsToVec(int argc, char** argv);
+
+/* Handles a list of flags. Flags are matched in the order given in case two
+ * flags match the same argument. Matched flags are removed, leaving only
+ * unmatched arguments. */
+bool ParseFlags(const std::vector<Flag>& flags, std::vector<std::string>& args);
+bool ParseFlags(const std::vector<Flag>& flags, std::vector<std::string>&&);
+
+bool WriteGflagsCompatXml(const std::vector<Flag>&, std::ostream&);
+
+/* If any of these are used, they should be evaluated after all other flags, and
+ * in the order defined here (help before invalid flags, invalid flags before
+ * unexpected arguments). */
+
+/* If a "-help" or "--help" flag is present, prints all the flags and fails. */
+Flag HelpFlag(const std::vector<Flag>& flags, const std::string& text = "");
+/* Catches unrecognized arguments that begin with `-`, and errors out. This
+ * effectively denies unknown flags. */
+Flag InvalidFlagGuard();
+/* Catches any arguments not extracted by other Flag matchers and errors out.
+ * This effectively denies unknown flags and any positional arguments. */
+Flag UnexpectedArgumentGuard();
+
+// Create a flag resembling a gflags argument of the given type. This includes
+// "-[-]flag=*",support for all types, "-[-]noflag" support for booleans, and
+// "-flag *", "--flag *", support for other types. The value passed in the flag
+// is saved to the defined reference.
+Flag GflagsCompatFlag(const std::string& name);
+Flag GflagsCompatFlag(const std::string& name, std::string& value);
+Flag GflagsCompatFlag(const std::string& name, std::int32_t& value);
+Flag GflagsCompatFlag(const std::string& name, bool& value);
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/flag_parser_test.cpp b/common/libs/utils/flag_parser_test.cpp
new file mode 100644
index 0000000..ab16c84
--- /dev/null
+++ b/common/libs/utils/flag_parser_test.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2021 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/flag_parser.h>
+
+#include <gtest/gtest.h>
+#include <libxml/tree.h>
+#include <map>
+#include <optional>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace cuttlefish {
+
+TEST(FlagParser, DuplicateAlias) {
+ FlagAlias alias = {FlagAliasMode::kFlagExact, "--flag"};
+ ASSERT_DEATH({ Flag().Alias(alias).Alias(alias); }, "Duplicate flag alias");
+}
+
+TEST(FlagParser, ConflictingAlias) {
+ FlagAlias exact_alias = {FlagAliasMode::kFlagExact, "--flag"};
+ FlagAlias following_alias = {FlagAliasMode::kFlagConsumesFollowing, "--flag"};
+ ASSERT_DEATH({ Flag().Alias(exact_alias).Alias(following_alias); },
+ "Overlapping flag aliases");
+}
+
+TEST(FlagParser, StringFlag) {
+ std::string value;
+ auto flag = GflagsCompatFlag("myflag", value);
+ ASSERT_TRUE(flag.Parse({"-myflag=a"}));
+ ASSERT_EQ(value, "a");
+ ASSERT_TRUE(flag.Parse({"--myflag=b"}));
+ ASSERT_EQ(value, "b");
+ ASSERT_TRUE(flag.Parse({"-myflag", "c"}));
+ ASSERT_EQ(value, "c");
+ ASSERT_TRUE(flag.Parse({"--myflag", "d"}));
+ ASSERT_EQ(value, "d");
+ ASSERT_TRUE(flag.Parse({"--myflag="}));
+ ASSERT_EQ(value, "");
+}
+
+std::optional<std::map<std::string, std::string>> flagXml(const Flag& f) {
+ std::stringstream xml_stream;
+ if (!f.WriteGflagsCompatXml(xml_stream)) {
+ return {};
+ }
+ auto xml = xml_stream.str();
+ // Holds all memory for the parsed structure.
+ std::unique_ptr<xmlDoc, xmlFreeFunc> doc(
+ xmlReadMemory(xml.c_str(), xml.size(), nullptr, nullptr, 0), xmlFree);
+ if (!doc) {
+ return {};
+ }
+ xmlNodePtr root_element = xmlDocGetRootElement(doc.get());
+ std::map<std::string, std::string> elements_map;
+ for (auto elem = root_element->children; elem != nullptr; elem = elem->next) {
+ if (elem->type != xmlElementType::XML_ELEMENT_NODE) {
+ continue;
+ }
+ elements_map[(char*)elem->name] = "";
+ if (elem->children == nullptr) {
+ continue;
+ }
+ if (elem->children->type != XML_TEXT_NODE) {
+ continue;
+ }
+ elements_map[(char*)elem->name] = (char*)elem->children->content;
+ }
+ return elements_map;
+}
+
+TEST(FlagParser, GflagsIncompatibleFlag) {
+ auto flag = Flag().Alias({FlagAliasMode::kFlagExact, "--flag"});
+ ASSERT_FALSE(flagXml(flag));
+}
+
+TEST(FlagParser, StringFlagXml) {
+ std::string value = "somedefault";
+ auto flag = GflagsCompatFlag("myflag", value).Help("somehelp");
+ auto xml = flagXml(flag);
+ ASSERT_TRUE(xml);
+ ASSERT_NE((*xml)["file"], "");
+ ASSERT_EQ((*xml)["name"], "myflag");
+ ASSERT_EQ((*xml)["meaning"], "somehelp");
+ ASSERT_EQ((*xml)["default"], "somedefault");
+ ASSERT_EQ((*xml)["current"], "somedefault");
+ ASSERT_EQ((*xml)["type"], "string");
+}
+
+TEST(FlagParser, RepeatedStringFlag) {
+ std::string value;
+ auto flag = GflagsCompatFlag("myflag", value);
+ ASSERT_TRUE(flag.Parse({"-myflag=a", "--myflag", "b"}));
+ ASSERT_EQ(value, "b");
+}
+
+TEST(FlagParser, RepeatedListFlag) {
+ std::vector<std::string> elems;
+ auto flag = GflagsCompatFlag("myflag");
+ flag.Setter([&elems](const FlagMatch& match) {
+ elems.push_back(match.value);
+ return true;
+ });
+ ASSERT_TRUE(flag.Parse({"-myflag=a", "--myflag", "b"}));
+ ASSERT_EQ(elems, (std::vector<std::string>{"a", "b"}));
+}
+
+TEST(FlagParser, FlagRemoval) {
+ std::string value;
+ auto flag = GflagsCompatFlag("myflag", value);
+ std::vector<std::string> flags = {"-myflag=a", "-otherflag=c"};
+ ASSERT_TRUE(flag.Parse(flags));
+ ASSERT_EQ(value, "a");
+ ASSERT_EQ(flags, std::vector<std::string>{"-otherflag=c"});
+ flags = {"-otherflag=a", "-myflag=c"};
+ ASSERT_TRUE(flag.Parse(flags));
+ ASSERT_EQ(value, "c");
+ ASSERT_EQ(flags, std::vector<std::string>{"-otherflag=a"});
+}
+
+TEST(FlagParser, IntFlag) {
+ std::int32_t value = 0;
+ auto flag = GflagsCompatFlag("myflag", value);
+ ASSERT_TRUE(flag.Parse({"-myflag=5"}));
+ ASSERT_EQ(value, 5);
+ ASSERT_TRUE(flag.Parse({"--myflag=6"}));
+ ASSERT_EQ(value, 6);
+ ASSERT_TRUE(flag.Parse({"-myflag", "7"}));
+ ASSERT_EQ(value, 7);
+ ASSERT_TRUE(flag.Parse({"--myflag", "8"}));
+ ASSERT_EQ(value, 8);
+}
+
+TEST(FlagParser, IntFlagXml) {
+ int value = 5;
+ auto flag = GflagsCompatFlag("myflag", value).Help("somehelp");
+ auto xml = flagXml(flag);
+ ASSERT_TRUE(xml);
+ ASSERT_NE((*xml)["file"], "");
+ ASSERT_EQ((*xml)["name"], "myflag");
+ ASSERT_EQ((*xml)["meaning"], "somehelp");
+ ASSERT_EQ((*xml)["default"], "5");
+ ASSERT_EQ((*xml)["current"], "5");
+ ASSERT_EQ((*xml)["type"], "string");
+}
+
+TEST(FlagParser, BoolFlag) {
+ bool value = false;
+ auto flag = GflagsCompatFlag("myflag", value);
+ ASSERT_TRUE(flag.Parse({"-myflag"}));
+ ASSERT_TRUE(value);
+
+ value = false;
+ ASSERT_TRUE(flag.Parse({"--myflag"}));
+ ASSERT_TRUE(value);
+
+ value = false;
+ ASSERT_TRUE(flag.Parse({"-myflag=true"}));
+ ASSERT_TRUE(value);
+
+ value = false;
+ ASSERT_TRUE(flag.Parse({"--myflag=true"}));
+ ASSERT_TRUE(value);
+
+ value = true;
+ ASSERT_TRUE(flag.Parse({"-nomyflag"}));
+ ASSERT_FALSE(value);
+
+ value = true;
+ ASSERT_TRUE(flag.Parse({"--nomyflag"}));
+ ASSERT_FALSE(value);
+
+ value = true;
+ ASSERT_TRUE(flag.Parse({"-myflag=false"}));
+ ASSERT_FALSE(value);
+
+ value = true;
+ ASSERT_TRUE(flag.Parse({"--myflag=false"}));
+ ASSERT_FALSE(value);
+
+ ASSERT_FALSE(flag.Parse({"--myflag=nonsense"}));
+}
+
+TEST(FlagParser, BoolFlagXml) {
+ bool value = true;
+ auto flag = GflagsCompatFlag("myflag", value).Help("somehelp");
+ auto xml = flagXml(flag);
+ ASSERT_TRUE(xml);
+ ASSERT_NE((*xml)["file"], "");
+ ASSERT_EQ((*xml)["name"], "myflag");
+ ASSERT_EQ((*xml)["meaning"], "somehelp");
+ ASSERT_EQ((*xml)["default"], "true");
+ ASSERT_EQ((*xml)["current"], "true");
+ ASSERT_EQ((*xml)["type"], "bool");
+}
+
+TEST(FlagParser, StringIntFlag) {
+ std::int32_t int_value = 0;
+ std::string string_value;
+ auto int_flag = GflagsCompatFlag("int", int_value);
+ auto string_flag = GflagsCompatFlag("string", string_value);
+ std::vector<Flag> flags = {int_flag, string_flag};
+ ASSERT_TRUE(ParseFlags(flags, {"-int=5", "-string=a"}));
+ ASSERT_EQ(int_value, 5);
+ ASSERT_EQ(string_value, "a");
+ ASSERT_TRUE(ParseFlags(flags, {"--int=6", "--string=b"}));
+ ASSERT_EQ(int_value, 6);
+ ASSERT_EQ(string_value, "b");
+ ASSERT_TRUE(ParseFlags(flags, {"-int", "7", "-string", "c"}));
+ ASSERT_EQ(int_value, 7);
+ ASSERT_EQ(string_value, "c");
+ ASSERT_TRUE(ParseFlags(flags, {"--int", "8", "--string", "d"}));
+ ASSERT_EQ(int_value, 8);
+ ASSERT_EQ(string_value, "d");
+}
+
+TEST(FlagParser, InvalidStringFlag) {
+ std::string value;
+ auto flag = GflagsCompatFlag("myflag", value);
+ ASSERT_FALSE(flag.Parse({"-myflag"}));
+ ASSERT_FALSE(flag.Parse({"--myflag"}));
+}
+
+TEST(FlagParser, InvalidIntFlag) {
+ int value;
+ auto flag = GflagsCompatFlag("myflag", value);
+ ASSERT_FALSE(flag.Parse({"-myflag"}));
+ ASSERT_FALSE(flag.Parse({"--myflag"}));
+ ASSERT_FALSE(flag.Parse({"-myflag=abc"}));
+ ASSERT_FALSE(flag.Parse({"--myflag=def"}));
+ ASSERT_FALSE(flag.Parse({"-myflag", "abc"}));
+ ASSERT_FALSE(flag.Parse({"--myflag", "def"}));
+}
+
+TEST(FlagParser, InvalidFlagGuard) {
+ auto flag = InvalidFlagGuard();
+ ASSERT_TRUE(flag.Parse({}));
+ ASSERT_TRUE(flag.Parse({"positional"}));
+ ASSERT_TRUE(flag.Parse({"positional", "positional2"}));
+ ASSERT_FALSE(flag.Parse({"-flag"}));
+ ASSERT_FALSE(flag.Parse({"-"}));
+}
+
+TEST(FlagParser, UnexpectedArgumentGuard) {
+ auto flag = UnexpectedArgumentGuard();
+ ASSERT_TRUE(flag.Parse({}));
+ ASSERT_FALSE(flag.Parse({"positional"}));
+ ASSERT_FALSE(flag.Parse({"positional", "positional2"}));
+ ASSERT_FALSE(flag.Parse({"-flag"}));
+ ASSERT_FALSE(flag.Parse({"-"}));
+}
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/network.cpp b/common/libs/utils/network.cpp
index d1f5f59..ba69bed 100644
--- a/common/libs/utils/network.cpp
+++ b/common/libs/utils/network.cpp
@@ -36,12 +36,6 @@
namespace cuttlefish {
namespace {
-static std::string DefaultHostArtifactsPath(const std::string& file_name) {
- return (StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) +
- "/") +
- file_name;
-}
-
// 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).
@@ -96,42 +90,28 @@
return tap_fd;
}
- if (HostArch() == Arch::Arm64) {
- auto tapsetiff_path = DefaultHostArtifactsPath("bin/tapsetiff");
- Command cmd(tapsetiff_path);
- cmd.AddParameter(tap_fd);
- cmd.AddParameter(interface_name.c_str());
- int ret = cmd.Start().Wait();
- if (ret != 0) {
- LOG(ERROR) << "Unable to run tapsetiff.py. Exited with status " << ret;
- tap_fd->Close();
- return SharedFD();
- }
- } else {
- 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);
+ 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 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);
+ 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 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;
}
diff --git a/common/libs/utils/shared_fd_flag.cpp b/common/libs/utils/shared_fd_flag.cpp
new file mode 100644
index 0000000..9d5ad89
--- /dev/null
+++ b/common/libs/utils/shared_fd_flag.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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/shared_fd_flag.h"
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <string>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/flag_parser.h"
+
+namespace cuttlefish {
+
+static bool Set(const FlagMatch& match, SharedFD& out) {
+ int raw_fd;
+ if (!android::base::ParseInt(match.value.c_str(), &raw_fd)) {
+ LOG(ERROR) << "Failed to parse value \"" << match.value
+ << "\" for fd flag \"" << match.key << "\"";
+ return false;
+ }
+ out = SharedFD::Dup(raw_fd);
+ if (out->IsOpen()) {
+ close(raw_fd);
+ }
+ return true;
+}
+
+Flag SharedFDFlag(SharedFD& out) {
+ return Flag().Setter([&](const FlagMatch& mat) { return Set(mat, out); });
+}
+Flag SharedFDFlag(const std::string& name, SharedFD& out) {
+ return GflagsCompatFlag(name).Setter(
+ [&out](const FlagMatch& mat) { return Set(mat, out); });
+}
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/size_utils.cpp b/common/libs/utils/shared_fd_flag.h
similarity index 69%
rename from common/libs/utils/size_utils.cpp
rename to common/libs/utils/shared_fd_flag.h
index 9f25445..33b1555 100644
--- a/common/libs/utils/size_utils.cpp
+++ b/common/libs/utils/shared_fd_flag.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-#include "common/libs/utils/size_utils.h"
+#include <string>
-#include <unistd.h>
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/flag_parser.h"
namespace cuttlefish {
-uint64_t AlignToPowerOf2(uint64_t val, uint8_t align_log) {
- uint64_t align = 1ULL << align_log;
- return ((val + (align - 1)) / align) * align;
-}
+Flag SharedFDFlag(SharedFD& out);
+Flag SharedFDFlag(const std::string& name, SharedFD& out);
} // namespace cuttlefish
diff --git a/common/libs/utils/size_utils.h b/common/libs/utils/size_utils.h
index 4158ddf..71bfea6 100644
--- a/common/libs/utils/size_utils.h
+++ b/common/libs/utils/size_utils.h
@@ -19,7 +19,16 @@
namespace cuttlefish {
+// Keep the full disk size a multiple of 64k, for crosvm's virtio_blk driver
+constexpr int DISK_SIZE_SHIFT = 16;
+
+// Keep all partitions 4k aligned, for host performance reasons
+constexpr int PARTITION_SIZE_SHIFT = 12;
+
// Returns the smallest multiple of 2^align_log greater than or equal to val.
-uint64_t AlignToPowerOf2(uint64_t val, uint8_t align_log);
+constexpr uint64_t AlignToPowerOf2(uint64_t val, uint8_t align_log) {
+ uint64_t align = 1ULL << align_log;
+ return ((val + (align - 1)) / align) * align;
+}
} // namespace cuttlefish
diff --git a/common/libs/utils/subprocess.cpp b/common/libs/utils/subprocess.cpp
index 7c05219..b2613c1 100644
--- a/common/libs/utils/subprocess.cpp
+++ b/common/libs/utils/subprocess.cpp
@@ -143,7 +143,7 @@
return retval;
}
-bool KillSubprocess(Subprocess* subprocess) {
+StopperResult KillSubprocess(Subprocess* subprocess) {
auto pid = subprocess->pid();
if (pid > 0) {
auto pgid = getpgid(pid);
@@ -155,13 +155,15 @@
// to the process and not a (non-existent) group
}
bool is_group_head = pid == pgid;
- if (is_group_head) {
- return killpg(pid, SIGKILL) == 0;
- } else {
- return kill(pid, SIGKILL) == 0;
+ auto kill_ret = (is_group_head ? killpg : kill)(pid, SIGKILL);
+ if (kill_ret == 0) {
+ return StopperResult::kStopSuccess;
}
+ auto kill_cmd = is_group_head ? "killpg(" : "kill(";
+ PLOG(ERROR) << kill_cmd << pid << ", SIGKILL) failed: ";
+ return StopperResult::kStopFailure;
}
- return true;
+ return StopperResult::kStopSuccess;
}
Command::~Command() {
@@ -175,41 +177,30 @@
}
}
-bool Command::BuildParameter(std::stringstream* stream, SharedFD shared_fd) {
+void Command::BuildParameter(std::stringstream* stream, SharedFD shared_fd) {
int fd;
if (inherited_fds_.count(shared_fd)) {
fd = inherited_fds_[shared_fd];
} else {
fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- LOG(ERROR) << "Could not acquire a new file descriptor: " << shared_fd->StrError();
- return false;
- }
+ CHECK(fd >= 0) << "Could not acquire a new file descriptor: "
+ << shared_fd->StrError();
inherited_fds_[shared_fd] = fd;
}
*stream << fd;
- return true;
}
-bool Command::RedirectStdIO(Subprocess::StdIOChannel channel,
+void Command::RedirectStdIO(Subprocess::StdIOChannel channel,
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;
- }
+ CHECK(shared_fd->IsOpen());
+ CHECK(redirects_.count(channel) == 0)
+ << "Attempted multiple redirections of fd: " << static_cast<int>(channel);
auto dup_fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
- if (dup_fd < 0) {
- LOG(ERROR) << "Could not acquire a new file descriptor: " << shared_fd->StrError();
- return false;
- }
+ CHECK(dup_fd >= 0) << "Could not acquire a new file descriptor: "
+ << shared_fd->StrError();
redirects_[channel] = dup_fd;
- return true;
}
-bool Command::RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
+void Command::RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
Subprocess::StdIOChannel parent_channel) {
return RedirectStdIO(subprocess_channel,
SharedFD::Dup(static_cast<int>(parent_channel)));
@@ -315,11 +306,7 @@
<< cmd.GetShortName() << "\"";
return -1;
}
- if (!cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, pipe_read)) {
- LOG(ERROR) << "Could not set stdout of \"" << cmd.GetShortName()
- << "\", was already set.";
- return -1;
- }
+ cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, pipe_read);
stdin_thread = std::thread([pipe_write, stdin, &io_error]() {
int written = WriteAll(pipe_write, *stdin);
if (written < 0) {
@@ -335,11 +322,7 @@
<< cmd.GetShortName() << "\"";
return -1;
}
- if (!cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, pipe_write)) {
- LOG(ERROR) << "Could not set stdout of \"" << cmd.GetShortName()
- << "\", was already set.";
- return -1;
- }
+ cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, pipe_write);
stdout_thread = std::thread([pipe_read, stdout, &io_error]() {
int read = ReadAll(pipe_read, stdout);
if (read < 0) {
@@ -355,11 +338,7 @@
<< cmd.GetShortName() << "\"";
return -1;
}
- if (!cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, pipe_write)) {
- LOG(ERROR) << "Could not set stderr of \"" << cmd.GetShortName()
- << "\", was already set.";
- return -1;
- }
+ cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, pipe_write);
stderr_thread = std::thread([pipe_read, stderr, &io_error]() {
int read = ReadAll(pipe_read, stderr);
if (read < 0) {
diff --git a/common/libs/utils/subprocess.h b/common/libs/utils/subprocess.h
index 777a026..50c86b9 100644
--- a/common/libs/utils/subprocess.h
+++ b/common/libs/utils/subprocess.h
@@ -29,12 +29,19 @@
#include <common/libs/fs/shared_fd.h>
namespace cuttlefish {
+
+enum class StopperResult {
+ kStopFailure, /* Failed to stop the subprocess. */
+ kStopCrash, /* Attempted to stop the subprocess cleanly, but that failed. */
+ kStopSuccess, /* The subprocess exited in the expected way. */
+};
+
class Command;
class Subprocess;
class SubprocessOptions;
-using SubprocessStopper = std::function<bool(Subprocess*)>;
+using SubprocessStopper = std::function<StopperResult(Subprocess*)>;
// Kills a process by sending it the SIGKILL signal.
-bool KillSubprocess(Subprocess* subprocess);
+StopperResult KillSubprocess(Subprocess* subprocess);
// Keeps track of a running (sub)process. Allows to wait for its completion.
// It's an error to wait twice for the same subprocess.
@@ -65,7 +72,7 @@
// completion of the command, that's what Wait is for.
bool Started() const { return started_; }
pid_t pid() const { return pid_; }
- bool Stop() { return stopper_(this); }
+ StopperResult Stop() { return stopper_(this); }
private:
// Copy is disabled to avoid waiting twice for the same pid (the first wait
@@ -108,15 +115,15 @@
private:
template <typename T>
// For every type other than SharedFD (for which there is a specialisation)
- bool BuildParameter(std::stringstream* stream, T t) {
+ void BuildParameter(std::stringstream* stream, T t) {
*stream << t;
- return true;
}
// Special treatment for SharedFD
- bool BuildParameter(std::stringstream* stream, SharedFD shared_fd);
+ void BuildParameter(std::stringstream* stream, SharedFD shared_fd);
template <typename T, typename... Args>
- bool BuildParameter(std::stringstream* stream, T t, Args... args) {
- return BuildParameter(stream, t) && BuildParameter(stream, args...);
+ void BuildParameter(std::stringstream* stream, T t, Args... args) {
+ BuildParameter(stream, t);
+ BuildParameter(stream, args...);
}
public:
@@ -136,6 +143,14 @@
Command& operator=(const Command&) = delete;
~Command();
+ const std::string& Executable() const { return command_[0]; }
+
+ void SetExecutable(const std::string& executable) {
+ command_[0] = executable;
+ }
+
+ void SetStopper(SubprocessStopper stopper) { subprocess_stopper_ = stopper; }
+
// Specify the environment for the subprocesses to be started. By default
// subprocesses inherit the parent's environment.
void SetEnvironment(const std::vector<std::string>& env) {
@@ -156,33 +171,24 @@
// object is destroyed. To add multiple parameters to the command the function
// must be called multiple times, one per parameter.
template <typename... Args>
- bool AddParameter(Args... args) {
+ void AddParameter(Args... args) {
std::stringstream ss;
- if (BuildParameter(&ss, args...)) {
- command_.push_back(ss.str());
- return true;
- }
- return false;
+ BuildParameter(&ss, args...);
+ command_.push_back(ss.str());
}
// Similar to AddParameter, except the args are appended to the last (most
// recently-added) parameter in the command.
template <typename... Args>
- bool AppendToLastParameter(Args... args) {
- if (command_.empty()) {
- LOG(ERROR) << "There is no parameter to append to.";
- return false;
- }
+ void AppendToLastParameter(Args... args) {
+ CHECK(!command_.empty()) << "There is no parameter to append to.";
std::stringstream ss;
- if (BuildParameter(&ss, args...)) {
- command_[command_.size()-1] += ss.str();
- return true;
- }
- return false;
+ BuildParameter(&ss, args...);
+ command_[command_.size() - 1] += ss.str();
}
// Redirects the standard IO of the command.
- bool RedirectStdIO(Subprocess::StdIOChannel channel, SharedFD shared_fd);
- bool RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
+ void RedirectStdIO(Subprocess::StdIOChannel channel, SharedFD shared_fd);
+ void RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
Subprocess::StdIOChannel parent_channel);
// Starts execution of the command. This method can be called multiple times,
diff --git a/common/libs/utils/vsock_connection.cpp b/common/libs/utils/vsock_connection.cpp
new file mode 100644
index 0000000..6142b69
--- /dev/null
+++ b/common/libs/utils/vsock_connection.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2021 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/vsock_connection.h"
+
+#include "common/libs/fs/shared_buf.h"
+#include "common/libs/fs/shared_select.h"
+
+#include "android-base/logging.h"
+
+namespace cuttlefish {
+
+VsockConnection::~VsockConnection() { Disconnect(); }
+
+std::future<bool> VsockConnection::ConnectAsync(unsigned int port,
+ unsigned int cid) {
+ return std::async(std::launch::async,
+ [this, port, cid]() { return Connect(port, cid); });
+}
+
+void VsockConnection::Disconnect() {
+ LOG(INFO) << "Disconnecting with fd status:" << fd_->StrError();
+ fd_->Shutdown(SHUT_RDWR);
+ if (disconnect_callback_) {
+ disconnect_callback_();
+ }
+ fd_->Close();
+}
+
+void VsockConnection::SetDisconnectCallback(std::function<void()> callback) {
+ disconnect_callback_ = callback;
+}
+
+bool VsockConnection::IsConnected() const { return fd_->IsOpen(); }
+
+bool VsockConnection::DataAvailable() const {
+ SharedFDSet read_set;
+ read_set.Set(fd_);
+ struct timeval timeout = {0, 0};
+ return Select(&read_set, nullptr, nullptr, &timeout) > 0;
+}
+
+int32_t VsockConnection::Read() {
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ int32_t result;
+ if (ReadExactBinary(fd_, &result) != sizeof(result)) {
+ Disconnect();
+ return 0;
+ }
+ return result;
+}
+
+bool VsockConnection::Read(std::vector<char>& data) {
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ return ReadExact(fd_, &data) == data.size();
+}
+
+std::vector<char> VsockConnection::Read(size_t size) {
+ if (size == 0) {
+ return {};
+ }
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ std::vector<char> result(size);
+ if (ReadExact(fd_, &result) != size) {
+ Disconnect();
+ return {};
+ }
+ return result;
+}
+
+std::future<std::vector<char>> VsockConnection::ReadAsync(size_t size) {
+ return std::async(std::launch::async, [this, size]() { return Read(size); });
+}
+
+// Message format is buffer size followed by buffer data
+std::vector<char> VsockConnection::ReadMessage() {
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ auto size = Read();
+ if (size < 0) {
+ Disconnect();
+ return {};
+ }
+ return Read(size);
+}
+
+bool VsockConnection::ReadMessage(std::vector<char>& data) {
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ auto size = Read();
+ if (size < 0) {
+ Disconnect();
+ return false;
+ }
+ data.resize(size);
+ return Read(data);
+}
+
+std::future<std::vector<char>> VsockConnection::ReadMessageAsync() {
+ return std::async(std::launch::async, [this]() { return ReadMessage(); });
+}
+
+Json::Value VsockConnection::ReadJsonMessage() {
+ auto msg = ReadMessage();
+ Json::CharReaderBuilder builder;
+ Json::CharReader* reader = builder.newCharReader();
+ Json::Value json_msg;
+ std::string errors;
+ if (!reader->parse(msg.data(), msg.data() + msg.size(), &json_msg, &errors)) {
+ return {};
+ }
+ return json_msg;
+}
+
+std::future<Json::Value> VsockConnection::ReadJsonMessageAsync() {
+ return std::async(std::launch::async, [this]() { return ReadJsonMessage(); });
+}
+
+bool VsockConnection::Write(int32_t data) {
+ std::lock_guard<std::recursive_mutex> lock(write_mutex_);
+ if (WriteAllBinary(fd_, &data) != sizeof(data)) {
+ Disconnect();
+ return false;
+ }
+ return true;
+}
+
+bool VsockConnection::Write(const char* data, unsigned int size) {
+ std::lock_guard<std::recursive_mutex> lock(write_mutex_);
+ if (WriteAll(fd_, data, size) != size) {
+ Disconnect();
+ return false;
+ }
+ return true;
+}
+
+bool VsockConnection::Write(const std::vector<char>& data) {
+ return Write(data.data(), data.size());
+}
+
+// Message format is buffer size followed by buffer data
+bool VsockConnection::WriteMessage(const std::string& data) {
+ return Write(data.size()) && Write(data.c_str(), data.length());
+}
+
+bool VsockConnection::WriteMessage(const std::vector<char>& data) {
+ std::lock_guard<std::recursive_mutex> lock(write_mutex_);
+ return Write(data.size()) && Write(data);
+}
+
+bool VsockConnection::WriteMessage(const Json::Value& data) {
+ Json::StreamWriterBuilder factory;
+ std::string message_str = Json::writeString(factory, data);
+ return WriteMessage(message_str);
+}
+
+bool VsockConnection::WriteStrides(const char* data, unsigned int size,
+ unsigned int num_strides, int stride_size) {
+ const char* src = data;
+ for (unsigned int i = 0; i < num_strides; ++i, src += stride_size) {
+ if (!Write(src, size)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VsockClientConnection::Connect(unsigned int port, unsigned int cid) {
+ fd_ = SharedFD::VsockClient(cid, port, SOCK_STREAM);
+ if (!fd_->IsOpen()) {
+ LOG(ERROR) << "Failed to connect:" << fd_->StrError();
+ }
+ return fd_->IsOpen();
+}
+
+VsockServerConnection::~VsockServerConnection() { ServerShutdown(); }
+
+void VsockServerConnection::ServerShutdown() {
+ if (server_fd_->IsOpen()) {
+ LOG(INFO) << __FUNCTION__
+ << ": server fd status:" << server_fd_->StrError();
+ server_fd_->Shutdown(SHUT_RDWR);
+ server_fd_->Close();
+ }
+}
+
+bool VsockServerConnection::Connect(unsigned int port, unsigned int cid) {
+ if (!server_fd_->IsOpen()) {
+ server_fd_ = cuttlefish::SharedFD::VsockServer(port, SOCK_STREAM, cid);
+ }
+ if (server_fd_->IsOpen()) {
+ fd_ = SharedFD::Accept(*server_fd_);
+ return fd_->IsOpen();
+ } else {
+ return false;
+ }
+}
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/vsock_connection.h b/common/libs/utils/vsock_connection.h
new file mode 100644
index 0000000..1a16b2f
--- /dev/null
+++ b/common/libs/utils/vsock_connection.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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 <json/json.h>
+#include <functional>
+#include <future>
+#include <mutex>
+#include <vector>
+#include "common/libs/fs/shared_fd.h"
+
+namespace cuttlefish {
+
+class VsockConnection {
+ public:
+ virtual ~VsockConnection();
+ virtual bool Connect(unsigned int port, unsigned int cid) = 0;
+ virtual void Disconnect();
+ std::future<bool> ConnectAsync(unsigned int port, unsigned int cid);
+ void SetDisconnectCallback(std::function<void()> callback);
+
+ bool IsConnected() const;
+ bool DataAvailable() const;
+
+ int32_t Read();
+ bool Read(std::vector<char>& data);
+ std::vector<char> Read(size_t size);
+ std::future<std::vector<char>> ReadAsync(size_t size);
+
+ bool ReadMessage(std::vector<char>& data);
+ std::vector<char> ReadMessage();
+ std::future<std::vector<char>> ReadMessageAsync();
+ Json::Value ReadJsonMessage();
+ std::future<Json::Value> ReadJsonMessageAsync();
+
+ bool Write(int32_t data);
+ bool Write(const char* data, unsigned int size);
+ bool Write(const std::vector<char>& data);
+ bool WriteMessage(const std::string& data);
+ bool WriteMessage(const std::vector<char>& data);
+ bool WriteMessage(const Json::Value& data);
+ bool WriteStrides(const char* data, unsigned int size,
+ unsigned int num_strides, int stride_size);
+
+ protected:
+ std::recursive_mutex read_mutex_;
+ std::recursive_mutex write_mutex_;
+ std::function<void()> disconnect_callback_;
+ SharedFD fd_;
+};
+
+class VsockClientConnection : public VsockConnection {
+ public:
+ bool Connect(unsigned int port, unsigned int cid) override;
+};
+
+class VsockServerConnection : public VsockConnection {
+ public:
+ virtual ~VsockServerConnection();
+ void ServerShutdown();
+ bool Connect(unsigned int port, unsigned int cid) override;
+
+ private:
+ SharedFD server_fd_;
+};
+
+} // namespace cuttlefish
diff --git a/default-permissions.xml b/default-permissions.xml
index bd2ed2c..038ffce 100644
--- a/default-permissions.xml
+++ b/default-permissions.xml
@@ -67,27 +67,6 @@
<permission name="android.permission.RECEIVE_SMS" fixed="false"/>
</exception>
- <exception
- package="com.google.android.projection.gearhead"
- sha256-cert-digest="FD:B0:0C:43:DB:DE:8B:51:CB:31:2A:A8:1D:3B:5F:A1:77:13:AD:B9:4B:28:F5:98:D7:7F:8E:B8:9D:AC:EE:DF">
- <!-- Gearhead legacy -->
- <permission name="android.permission.ACCESS_FINE_LOCATION" fixed="false"/>
- <permission name="android.permission.CALL_PHONE" fixed="false"/>
- <permission name="android.permission.READ_CALL_LOG" fixed="false"/>
- <permission name="android.permission.READ_CONTACTS" fixed="false"/>
- <permission name="android.permission.READ_PHONE_STATE" fixed="false"/>
- <permission name="android.permission.RECEIVE_SMS" fixed="false"/>
- <permission name="android.permission.RECORD_AUDIO" fixed="false"/>
- <permission name="android.permission.SEND_SMS" fixed="false"/>
- <permission name="android.permission.READ_CALENDAR" fixed="false"/>
- <!-- For Top Gear -->
- <permission name="android.permission.PROCESS_OUTGOING_CALLS" fixed="false"/>
- <permission name="android.permission.READ_SMS" fixed="false"/>
- <permission name="android.permission.RECEIVE_MMS" fixed="false"/>
- <permission name="android.permission.WRITE_CALL_LOG" fixed="false"/>
- <permission name="android.permission.ACCESS_COARSE_LOCATION" fixed="false"/>
- </exception>
-
<exception package="com.google.android.settings.intelligence">
<!-- Calendar -->
<permission name="android.permission.READ_CALENDAR" fixed="true"/>
diff --git a/guest/Android.mk b/guest/Android.mk
new file mode 100644
index 0000000..428f4b5
--- /dev/null
+++ b/guest/Android.mk
@@ -0,0 +1,18 @@
+# Copyright (C) 2021 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)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/commands/bt_vhci_forwarder/Android.bp b/guest/commands/bt_vhci_forwarder/Android.bp
index 72dd7c9..75e006e 100644
--- a/guest/commands/bt_vhci_forwarder/Android.bp
+++ b/guest/commands/bt_vhci_forwarder/Android.bp
@@ -6,6 +6,7 @@
name: "bt_vhci_forwarder",
srcs: [
"main.cpp",
+ ":H4PacketizerSources",
],
shared_libs: [
"libbase",
@@ -14,7 +15,12 @@
],
static_libs: [
"libgflags",
- "libbt-rootcanal",
+ ],
+ include_dirs: [
+ "system/bt/vendor_libs/test_vendor_lib/include",
+ "system/bt/vendor_libs/test_vendor_lib",
+ "system/bt",
+ "system/bt/gd",
],
defaults: ["cuttlefish_guest_only"]
}
diff --git a/host/commands/tapsetiff/Android.bp b/guest/commands/dlkm_loader/Android.bp
similarity index 68%
rename from host/commands/tapsetiff/Android.bp
rename to guest/commands/dlkm_loader/Android.bp
index 1d7dedb..ae91c02 100644
--- a/host/commands/tapsetiff/Android.bp
+++ b/guest/commands/dlkm_loader/Android.bp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2020 The Android Open Source Project
+// Copyright (C) 2021 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.
@@ -17,7 +17,17 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-sh_binary_host {
- name: "tapsetiff",
- src: "tapsetiff.py",
+cc_binary {
+ name: "dlkm_loader",
+ srcs: [
+ "dlkm_loader.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libmodprobe",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ defaults: ["cuttlefish_guest_only"]
}
diff --git a/guest/commands/dlkm_loader/dlkm_loader.cpp b/guest/commands/dlkm_loader/dlkm_loader.cpp
new file mode 100644
index 0000000..0d22225
--- /dev/null
+++ b/guest/commands/dlkm_loader/dlkm_loader.cpp
@@ -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.
+ */
+
+#include <android-base/logging.h>
+#include <modprobe/modprobe.h>
+
+int main(void) {
+ LOG(INFO) << "dlkm loader successfully initialized";
+ Modprobe m({"/vendor/lib/modules"}, "modules.load");
+ CHECK(m.LoadListedModules(true)) << "modules from vendor dlkm weren't loaded correctly";
+ LOG(INFO) << "module load count is " << m.GetModuleCount();
+ return 0;
+}
diff --git a/guest/commands/rotate/main.cpp b/guest/commands/rotate/main.cpp
deleted file mode 100644
index 8967097..0000000
--- a/guest/commands/rotate/main.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/chrono_utils.h>
-#include <android-base/logging.h>
-#include <binder/IServiceManager.h>
-#include <utils/StrongPointer.h>
-#include <utils/SystemClock.h>
-
-#include <thread>
-
-#include "android/hardware/sensors/2.0/ISensors.h"
-
-using android::sp;
-using android::hardware::sensors::V1_0::Event;
-using android::hardware::sensors::V1_0::OperationMode;
-using android::hardware::sensors::V1_0::Result;
-using android::hardware::sensors::V1_0::SensorInfo;
-using android::hardware::sensors::V1_0::SensorStatus;
-using android::hardware::sensors::V1_0::SensorType;
-using android::hardware::sensors::V2_0::ISensors;
-
-void InjectOrientation(bool portrait) {
- const sp<ISensors> sensors = ISensors::getService();
- if (sensors == nullptr) {
- LOG(FATAL) << "Unable to get ISensors.";
- }
-
- Result result;
-
- // Place the ISensors HAL into DATA_INJECTION mode so that we can
- // inject events.
- result = sensors->setOperationMode(OperationMode::DATA_INJECTION);
- if (result != Result::OK) {
- LOG(FATAL) << "Unable to set ISensors operation mode to DATA_INJECTION: "
- << toString(result);
- }
-
- // Find the first available accelerometer sensor.
- int accel_handle = -1;
- const auto& getSensorsList_result =
- sensors->getSensorsList([&](const auto& list) {
- for (const SensorInfo& sensor : list) {
- if (sensor.type == SensorType::ACCELEROMETER) {
- accel_handle = sensor.sensorHandle;
- break;
- }
- }
- });
- if (!getSensorsList_result.isOk()) {
- LOG(FATAL) << "Unable to get ISensors sensors list: "
- << getSensorsList_result.description();
- }
- if (accel_handle == -1) {
- LOG(FATAL) << "Unable to find ACCELEROMETER sensor.";
- }
-
- // Create a base ISensors accelerometer event.
- Event event;
- event.sensorHandle = accel_handle;
- event.sensorType = SensorType::ACCELEROMETER;
- if (portrait) {
- event.u.vec3.x = 0;
- event.u.vec3.y = 9.2;
- } else {
- event.u.vec3.x = 9.2;
- event.u.vec3.y = 0;
- }
- event.u.vec3.z = 3.5;
- event.u.vec3.status = SensorStatus::ACCURACY_HIGH;
-
- // Repeatedly inject accelerometer events. The WindowManager orientation
- // listener responds to sustained accelerometer data, not just a single event.
- android::base::Timer timer;
- while (timer.duration() < 1s) {
- event.timestamp = android::elapsedRealtimeNano();
- result = sensors->injectSensorData(event);
- if (result != Result::OK) {
- LOG(FATAL) << "Unable to inject ISensors accelerometer event: "
- << toString(result);
- }
- std::this_thread::sleep_for(10ms);
- }
-
- // Return the ISensors HAL back to NORMAL mode.
- result = sensors->setOperationMode(OperationMode::NORMAL);
- if (result != Result::OK) {
- LOG(FATAL) << "Unable to set sensors operation mode to NORMAL: "
- << toString(result);
- }
-}
-
-int main(int argc, char** argv) {
- if (argc == 1) {
- LOG(FATAL) << "Expected command line arg 'portrait' or 'landscape'";
- }
-
- bool portrait = true;
- if (!strcmp(argv[1], "portrait")) {
- portrait = true;
- } else if (!strcmp(argv[1], "landscape")) {
- portrait = false;
- } else {
- LOG(FATAL) << "Expected command line arg 'portrait' or 'landscape'";
- }
-
- InjectOrientation(portrait);
-}
diff --git a/guest/commands/rotate/Android.bp b/guest/commands/sensor_injection/Android.bp
similarity index 79%
rename from guest/commands/rotate/Android.bp
rename to guest/commands/sensor_injection/Android.bp
index 6568c0e..a29250a 100644
--- a/guest/commands/rotate/Android.bp
+++ b/guest/commands/sensor_injection/Android.bp
@@ -3,11 +3,11 @@
}
cc_binary {
- name: "cuttlefish_rotate",
+ name: "cuttlefish_sensor_injection",
srcs: ["main.cpp"],
shared_libs: [
"android.hardware.sensors@1.0",
- "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
"libbase",
"libbinder",
"libhidlbase",
diff --git a/guest/commands/sensor_injection/main.cpp b/guest/commands/sensor_injection/main.cpp
new file mode 100644
index 0000000..6eadb4a
--- /dev/null
+++ b/guest/commands/sensor_injection/main.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <utils/StrongPointer.h>
+#include <utils/SystemClock.h>
+
+#include <thread>
+
+#include "android/hardware/sensors/2.1/ISensors.h"
+
+using android::sp;
+using android::hardware::sensors::V1_0::OperationMode;
+using android::hardware::sensors::V1_0::Result;
+using android::hardware::sensors::V1_0::SensorStatus;
+using android::hardware::sensors::V2_1::Event;
+using android::hardware::sensors::V2_1::ISensors;
+using android::hardware::sensors::V2_1::SensorInfo;
+using android::hardware::sensors::V2_1::SensorType;
+
+sp<ISensors> startSensorInjection() {
+ const sp<ISensors> sensors = ISensors::getService();
+ if (sensors == nullptr) {
+ LOG(FATAL) << "Unable to get ISensors.";
+ }
+
+ // Place the ISensors HAL into DATA_INJECTION mode so that we can
+ // inject events.
+ Result result = sensors->setOperationMode(OperationMode::DATA_INJECTION);
+ if (result != Result::OK) {
+ LOG(FATAL) << "Unable to set ISensors operation mode to DATA_INJECTION: "
+ << toString(result);
+ }
+
+ return sensors;
+}
+
+int getSensorHandle(SensorType type, const sp<ISensors> sensors) {
+ // Find the first available sensor of the given type.
+ int handle = -1;
+ const auto& getSensorsList_result =
+ sensors->getSensorsList_2_1([&](const auto& list) {
+ for (const SensorInfo& sensor : list) {
+ if (sensor.type == type) {
+ handle = sensor.sensorHandle;
+ break;
+ }
+ }
+ });
+ if (!getSensorsList_result.isOk()) {
+ LOG(FATAL) << "Unable to get ISensors sensors list: "
+ << getSensorsList_result.description();
+ }
+ if (handle == -1) {
+ LOG(FATAL) << "Unable to find sensor.";
+ }
+ return handle;
+}
+
+void endSensorInjection(const sp<ISensors> sensors) {
+ // Return the ISensors HAL back to NORMAL mode.
+ Result result = sensors->setOperationMode(OperationMode::NORMAL);
+ if (result != Result::OK) {
+ LOG(FATAL) << "Unable to set sensors operation mode to NORMAL: "
+ << toString(result);
+ }
+}
+
+// Inject ACCELEROMETER events to corresponding to a given physical
+// device orientation: portrait or landscape.
+void InjectOrientation(bool portrait) {
+ sp<ISensors> sensors = startSensorInjection();
+ int handle = getSensorHandle(SensorType::ACCELEROMETER, sensors);
+
+ // Create a base ISensors accelerometer event.
+ Event event;
+ event.sensorHandle = handle;
+ event.sensorType = SensorType::ACCELEROMETER;
+ if (portrait) {
+ event.u.vec3.x = 0;
+ event.u.vec3.y = 9.2;
+ } else {
+ event.u.vec3.x = 9.2;
+ event.u.vec3.y = 0;
+ }
+ event.u.vec3.z = 3.5;
+ event.u.vec3.status = SensorStatus::ACCURACY_HIGH;
+
+ // Repeatedly inject accelerometer events. The WindowManager orientation
+ // listener responds to sustained accelerometer data, not just a single event.
+ android::base::Timer timer;
+ Result result;
+ while (timer.duration() < 1s) {
+ event.timestamp = android::elapsedRealtimeNano();
+ result = sensors->injectSensorData_2_1(event);
+ if (result != Result::OK) {
+ LOG(FATAL) << "Unable to inject ISensors accelerometer event: "
+ << toString(result);
+ }
+ std::this_thread::sleep_for(10ms);
+ }
+
+ endSensorInjection(sensors);
+}
+
+// Inject a single HINGE_ANGLE event at the given angle.
+void InjectHingeAngle(int angle) {
+ sp<ISensors> sensors = startSensorInjection();
+ int handle = getSensorHandle(SensorType::HINGE_ANGLE, sensors);
+
+ // Create a base ISensors hinge_angle event.
+ Event event;
+ event.sensorHandle = handle;
+ event.sensorType = SensorType::HINGE_ANGLE;
+ event.u.scalar = angle;
+ event.u.vec3.status = SensorStatus::ACCURACY_HIGH;
+ event.timestamp = android::elapsedRealtimeNano();
+ Result result = sensors->injectSensorData_2_1(event);
+ if (result != Result::OK) {
+ LOG(FATAL) << "Unable to inject HINGE_ANGLE data: " << toString(result);
+ }
+
+ endSensorInjection(sensors);
+}
+
+int main(int argc, char** argv) {
+ if (argc == 2) {
+ LOG(FATAL) << "Expected command line args 'rotate <portrait|landscape>' or "
+ "'hinge_angle <value>'";
+ }
+
+ if (!strcmp(argv[1], "rotate")) {
+ bool portrait = true;
+ if (!strcmp(argv[2], "portrait")) {
+ portrait = true;
+ } else if (!strcmp(argv[2], "landscape")) {
+ portrait = false;
+ } else {
+ LOG(FATAL) << "Expected command line arg 'portrait' or 'landscape'";
+ }
+ InjectOrientation(portrait);
+ } else if (!strcmp(argv[1], "hinge_angle")) {
+ int angle = std::stoi(argv[2]);
+ if (angle < 0 || angle > 360) {
+ LOG(FATAL) << "Bad hinge_angle value: " << argv[2];
+ }
+ InjectHingeAngle(angle);
+ } else {
+ LOG(FATAL) << "Unknown arg: " << argv[1];
+ }
+}
diff --git a/guest/commands/setup_wifi/main.cpp b/guest/commands/setup_wifi/main.cpp
index 0a07c97..310db79 100644
--- a/guest/commands/setup_wifi/main.cpp
+++ b/guest/commands/setup_wifi/main.cpp
@@ -32,17 +32,15 @@
#include "common/libs/net/network_interface.h"
#include "common/libs/net/network_interface_manager.h"
-DEFINE_string(mac_address, "", "mac address to use for wlan0");
+DEFINE_string(mac_prefix, "", "mac prefix to use for wlan0");
-static std::array<unsigned char, 6> str_to_mac(const std::string& mac_str) {
+static std::array<unsigned char, 6> prefix_to_mac(
+ const std::string& mac_prefix) {
std::array<unsigned char, 6> mac;
- std::istringstream stream(mac_str);
- for (int i = 0; i < 6; i++) {
- int num;
- stream >> std::hex >> num;
- mac[i] = num;
- stream.get();
- }
+ int macPrefix = stoi(mac_prefix);
+ mac[0] = 0x02;
+ mac[1] = (macPrefix >> CHAR_BIT) & 0xFF;
+ mac[2] = macPrefix & 0xFF;
return mac;
}
@@ -51,7 +49,8 @@
auto factory = cuttlefish::NetlinkClientFactory::Default();
std::unique_ptr<cuttlefish::NetlinkClient> nl(factory->New(NETLINK_ROUTE));
- LOG(INFO) << "Setting " << source << " mac address to " << FLAGS_mac_address;
+ LOG(INFO) << "Setting " << source << " mac address based on "
+ << FLAGS_mac_prefix;
int32_t index = if_nametoindex(source.c_str());
// Setting the address is available in RTM_SETLINK, but not RTM_NEWLINK.
// https://elixir.bootlin.com/linux/v5.4.44/source/net/core/rtnetlink.c#L2785
@@ -64,7 +63,7 @@
.ifi_index = index,
.ifi_change = 0xFFFFFFFF,
});
- fix_mac_request.AddMacAddress(str_to_mac(FLAGS_mac_address));
+ fix_mac_request.AddMacAddress(prefix_to_mac(FLAGS_mac_prefix));
bool fix_mac = nl->Send(fix_mac_request);
if (!fix_mac) {
LOG(ERROR) << "setup_network: could not fix mac address";
@@ -131,17 +130,17 @@
}
int main(int argc, char** argv) {
- char wifi_address[PROPERTY_VALUE_MAX + 1];
- property_get("ro.boot.wifi_mac_address", wifi_address, "");
+ char wifi_mac_prefix[PROPERTY_VALUE_MAX + 1];
+ property_get("ro.boot.wifi_mac_prefix", wifi_mac_prefix, "");
- SetCommandLineOptionWithMode("mac_address", wifi_address,
+ SetCommandLineOptionWithMode("mac_prefix", wifi_mac_prefix,
google::FlagSettingMode::SET_FLAGS_DEFAULT);
gflags::ParseCommandLineFlags(&argc, &argv, true);
- int renamed_eth0 = RenameNetwork("eth0", "buried_eth0");
- if (renamed_eth0 != 0) {
- return renamed_eth0;
+ int renamed_eth1 = RenameNetwork("eth1", "buried_eth0");
+ if (renamed_eth1 != 0) {
+ return renamed_eth1;
}
return CreateWifiWrapper("buried_eth0", "wlan0");
}
diff --git a/guest/hals/audio/Android.bp b/guest/hals/audio/Android.bp
deleted file mode 100644
index c6faae7..0000000
--- a/guest/hals/audio/Android.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-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
deleted file mode 100644
index 652d747..0000000
--- a/guest/hals/audio/audio_hw.c
+++ /dev/null
@@ -1,1848 +0,0 @@
-/*
- * 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/list.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 10
-#define OUT_PERIOD_COUNT 4
-
-#define IN_PERIOD_MS 10
-#define IN_PERIOD_COUNT 4
-
-struct generic_audio_device {
- struct audio_hw_device device; // Constant after init
- pthread_mutex_t lock;
- bool mic_mute; // Protected by this->lock
- struct mixer* mixer; // Protected by this->lock
- struct listnode out_streams; // Record for output streams, protected by this->lock
- struct listnode in_streams; // Record for input streams, protected by this->lock
- audio_patch_handle_t next_patch_handle; // Protected 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
- uint32_t num_devices; // Protected by this->lock
- audio_devices_t devices[AUDIO_PATCH_PORTS_MAX]; // 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
-
- audio_io_handle_t handle; // Constant after init
- audio_patch_handle_t patch_handle; // Protected by this->dev->lock
-
- struct listnode stream_node; // Protected by this->dev->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
-
- audio_io_handle_t handle; // Constant after init
- audio_patch_handle_t patch_handle; // Protected by this->dev->lock
-
- struct listnode stream_node; // Protected by this->dev->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(s): ",
- out_get_sample_rate(stream),
- out_get_buffer_size(stream),
- out_get_channels(stream),
- out_get_format(stream));
- if (out->num_devices == 0) {
- dprintf(fd, "%08x\n", AUDIO_DEVICE_NONE);
- } else {
- for (uint32_t i = 0; i < out->num_devices; i++) {
- if (i != 0) {
- dprintf(fd, ", ");
- }
- dprintf(fd, "%08x", out->devices[i]);
- }
- dprintf(fd, "\n");
- }
- dprintf(fd, "\t\taudio dev: %p\n\n", out->dev);
- pthread_mutex_unlock(&out->lock);
- return 0;
-}
-
-static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
-{
- struct str_parms *parms;
- char value[32];
- int success;
- int ret = -EINVAL;
-
- if (kvpairs == NULL || kvpairs[0] == 0) {
- return 0;
- }
- parms = str_parms_create_str(kvpairs);
- success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
- value, sizeof(value));
- // As the hal version is 3.0, it must not use set parameters API to set audio devices.
- // Instead, it should use create_audio_patch API.
- assert(("Must not use set parameters API to set audio devices", success < 0));
-
- if (str_parms_has_key(parms, AUDIO_PARAMETER_STREAM_FORMAT)) {
- // match the return value of out_set_format
- ret = -ENOSYS;
- }
-
- str_parms_destroy(parms);
-
- if (ret == -EINVAL) {
- ALOGW("%s(), unsupported parameter %s", __func__, kvpairs);
- // There is not any key supported for set_parameters API.
- // Return error when there is non-null value passed in.
- }
- 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);
- audio_devices_t device = AUDIO_DEVICE_NONE;
- for (uint32_t i = 0; i < out->num_devices; i++) {
- device |= out->devices[i];
- }
- str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, 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 = 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 str_parms *parms;
- char value[32];
- int success;
- int ret = -EINVAL;
-
- if (kvpairs == NULL || kvpairs[0] == 0) {
- return 0;
- }
- parms = str_parms_create_str(kvpairs);
- success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
- value, sizeof(value));
- // As the hal version is 3.0, it must not use set parameters API to set audio device.
- // Instead, it should use create_audio_patch API.
- assert(("Must not use set parameters API to set audio devices", success < 0));
-
- if (str_parms_has_key(parms, AUDIO_PARAMETER_STREAM_FORMAT)) {
- // match the return value of in_set_format
- ret = -ENOSYS;
- }
-
- str_parms_destroy(parms);
-
- if (ret == -EINVAL) {
- ALOGW("%s(), unsupported parameter %s", __func__, kvpairs);
- // There is not any key supported for set_parameters API.
- // Return error when there is non-null value passed in.
- }
- 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 = 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;
-
- out->handle = handle;
-
- pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
- out->dev = adev;
- // Only 1 device is expected despite the argument being named 'devices'
- out->num_devices = 1;
- out->devices[0] = 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);
-
- }
-
- pthread_mutex_lock(&adev->lock);
- list_add_tail(&adev->out_streams, &out->stream_node);
- pthread_mutex_unlock(&adev->lock);
-
- *stream_out = &out->stream;
-
-error:
-
- return ret;
-}
-
-// This must be called with adev->lock held.
-struct generic_stream_out *get_stream_out_by_io_handle_l(
- struct generic_audio_device *adev, audio_io_handle_t handle) {
- struct listnode *node;
-
- list_for_each(node, &adev->out_streams) {
- struct generic_stream_out *out = node_to_item(
- node, struct generic_stream_out, stream_node);
- if (out->handle == handle) {
- return out;
- }
- }
- return NULL;
-}
-
-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);
-
- struct generic_audio_device *adev = (struct generic_audio_device *) dev;
- pthread_mutex_lock(&adev->lock);
- list_remove(&out->stream_node);
- pthread_mutex_unlock(&adev->lock);
- 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_get_audio_port(struct audio_hw_device *dev,
- struct audio_port *port)
-{
- return 0;
-}
-
-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);
-}
-
-// This must be called with adev->lock held.
-struct generic_stream_in *get_stream_in_by_io_handle_l(
- struct generic_audio_device *adev, audio_io_handle_t handle) {
- struct listnode *node;
-
- list_for_each(node, &adev->in_streams) {
- struct generic_stream_in *in = node_to_item(
- node, struct generic_stream_in, stream_node);
- if (in->handle == handle) {
- return in;
- }
- }
- return NULL;
-}
-
-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);
-
- struct generic_audio_device *adev = (struct generic_audio_device *) dev;
- pthread_mutex_lock(&adev->lock);
- list_remove(&in->stream_node);
- pthread_mutex_unlock(&adev->lock);
- 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;
- uint32_t orig_sample_rate = config->sample_rate;
- audio_format_t orig_audio_format = config->format;
- audio_channel_mask_t orig_channel_mask = config->channel_mask;
- 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",
- orig_audio_format, orig_channel_mask, orig_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);
- }
- in->handle = handle;
-
- pthread_mutex_lock(&adev->lock);
- list_add_tail(&adev->in_streams, &in->stream_node);
- pthread_mutex_unlock(&adev->lock);
-
- *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 = 0;
- 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 = 0;
- return 0;
-}
-
-static int adev_create_audio_patch(struct audio_hw_device *dev,
- unsigned int num_sources,
- const struct audio_port_config *sources,
- unsigned int num_sinks,
- const struct audio_port_config *sinks,
- audio_patch_handle_t *handle) {
- if (num_sources != 1 || num_sinks == 0 || num_sinks > AUDIO_PATCH_PORTS_MAX) {
- return -EINVAL;
- }
-
- if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
- // If source is a device, the number of sinks should be 1.
- if (num_sinks != 1 || sinks[0].type != AUDIO_PORT_TYPE_MIX) {
- return -EINVAL;
- }
- } else if (sources[0].type == AUDIO_PORT_TYPE_MIX) {
- // If source is a mix, all sinks should be device.
- for (unsigned int i = 0; i < num_sinks; i++) {
- if (sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
- ALOGE("%s() invalid sink type %#x for mix source", __func__, sinks[i].type);
- return -EINVAL;
- }
- }
- } else {
- // All other cases are invalid.
- return -EINVAL;
- }
-
- struct generic_audio_device* adev = (struct generic_audio_device*) dev;
- int ret = 0;
- bool generatedPatchHandle = false;
- pthread_mutex_lock(&adev->lock);
- if (*handle == AUDIO_PATCH_HANDLE_NONE) {
- *handle = ++adev->next_patch_handle;
- generatedPatchHandle = true;
- }
-
- // Only handle patches for mix->devices and device->mix case.
- if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
- struct generic_stream_in *in =
- get_stream_in_by_io_handle_l(adev, sinks[0].ext.mix.handle);
- if (in == NULL) {
- ALOGE("%s()can not find stream with handle(%d)", __func__, sources[0].ext.mix.handle);
- ret = -EINVAL;
- goto error;
- }
-
- // Check if the patch handle match the recorded one if a valid patch handle is passed.
- if (!generatedPatchHandle && in->patch_handle != *handle) {
- ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream "
- "with handle(%d) when creating audio patch for device->mix",
- __func__, *handle, in->patch_handle, in->handle);
- ret = -EINVAL;
- goto error;
- }
- pthread_mutex_lock(&in->lock);
- in->device = sources[0].ext.device.type;
- pthread_mutex_unlock(&in->lock);
- in->patch_handle = *handle;
- } else {
- struct generic_stream_out *out =
- get_stream_out_by_io_handle_l(adev, sources[0].ext.mix.handle);
- if (out == NULL) {
- ALOGE("%s()can not find stream with handle(%d)", __func__, sources[0].ext.mix.handle);
- ret = -EINVAL;
- goto error;
- }
-
- // Check if the patch handle match the recorded one if a valid patch handle is passed.
- if (!generatedPatchHandle && out->patch_handle != *handle) {
- ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream "
- "with handle(%d) when creating audio patch for mix->device",
- __func__, *handle, out->patch_handle, out->handle);
- ret = -EINVAL;
- pthread_mutex_unlock(&out->lock);
- goto error;
- }
- pthread_mutex_lock(&out->lock);
- for (out->num_devices = 0; out->num_devices < num_sinks; out->num_devices++) {
- out->devices[out->num_devices] = sinks[out->num_devices].ext.device.type;
- }
- pthread_mutex_unlock(&out->lock);
- out->patch_handle = *handle;
- }
-
-error:
- if (ret != 0 && generatedPatchHandle) {
- *handle = AUDIO_PATCH_HANDLE_NONE;
- }
- pthread_mutex_unlock(&adev->lock);
- return 0;
-}
-
-// This must be called with adev->lock held.
-struct generic_stream_out *get_stream_out_by_patch_handle_l(
- struct generic_audio_device *adev, audio_patch_handle_t patch_handle) {
- struct listnode *node;
-
- list_for_each(node, &adev->out_streams) {
- struct generic_stream_out *out = node_to_item(
- node, struct generic_stream_out, stream_node);
- if (out->patch_handle == patch_handle) {
- return out;
- }
- }
- return NULL;
-}
-
-// This must be called with adev->lock held.
-struct generic_stream_in *get_stream_in_by_patch_handle_l(
- struct generic_audio_device *adev, audio_patch_handle_t patch_handle) {
- struct listnode *node;
-
- list_for_each(node, &adev->in_streams) {
- struct generic_stream_in *in = node_to_item(
- node, struct generic_stream_in, stream_node);
- if (in->patch_handle == patch_handle) {
- return in;
- }
- }
- return NULL;
-}
-
-static int adev_release_audio_patch(struct audio_hw_device *dev,
- audio_patch_handle_t patch_handle) {
- struct generic_audio_device *adev = (struct generic_audio_device *) dev;
-
- pthread_mutex_lock(&adev->lock);
- struct generic_stream_out *out = get_stream_out_by_patch_handle_l(adev, patch_handle);
- if (out != NULL) {
- pthread_mutex_lock(&out->lock);
- out->num_devices = 0;
- memset(out->devices, 0, sizeof(out->devices));
- pthread_mutex_unlock(&out->lock);
- out->patch_handle = AUDIO_PATCH_HANDLE_NONE;
- pthread_mutex_unlock(&adev->lock);
- return 0;
- }
- struct generic_stream_in *in = get_stream_in_by_patch_handle_l(adev, patch_handle);
- if (in != NULL) {
- pthread_mutex_lock(&in->lock);
- in->device = AUDIO_DEVICE_NONE;
- pthread_mutex_unlock(&in->lock);
- in->patch_handle = AUDIO_PATCH_HANDLE_NONE;
- pthread_mutex_unlock(&adev->lock);
- return 0;
- }
-
- pthread_mutex_unlock(&adev->lock);
- ALOGW("%s() cannot find stream for patch handle: %d", __func__, patch_handle);
- return -EINVAL;
-}
-
-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_3_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_audio_port = adev_get_audio_port; // 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;
- adev->device.create_audio_patch = adev_create_audio_patch;
- adev->device.release_audio_patch = adev_release_audio_patch;
-
- *device = &adev->device.common;
-
- adev->next_patch_handle = AUDIO_PATCH_HANDLE_NONE;
- list_init(&adev->out_streams);
- list_init(&adev->in_streams);
-
- 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/bt/OWNERS b/guest/hals/bt/OWNERS
index e8a4a00..e791d83 100644
--- a/guest/hals/bt/OWNERS
+++ b/guest/hals/bt/OWNERS
@@ -1,2 +1,3 @@
+include device/google/cuttlefish:/OWNERS
include platform/system/bt:/OWNERS
jeongik@google.com
\ No newline at end of file
diff --git a/guest/hals/camera/Android.bp b/guest/hals/camera/Android.bp
new file mode 100644
index 0000000..76212ba
--- /dev/null
+++ b/guest/hals/camera/Android.bp
@@ -0,0 +1,83 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+ name: "android.hardware.camera.provider@2.6-external-vsock-service",
+ defaults: ["hidl_defaults"],
+ proprietary: true,
+ relative_install_path: "hw",
+ srcs: ["external-service.cpp"],
+ compile_multilib: "first",
+ init_rc: ["android.hardware.camera.provider@2.6-external-vsock-service.rc"],
+ shared_libs: [
+ "android.hardware.camera.provider@2.6",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+}
+
+cc_library_shared {
+ name: "android.hardware.camera.provider@2.6-impl-cuttlefish",
+ defaults: ["hidl_defaults"],
+ proprietary: true,
+ relative_install_path: "hw",
+ srcs: [
+ "vsock_camera_provider_2_6.cpp",
+ "vsock_camera_device_3_4.cpp",
+ "vsock_camera_device_session_3_4.cpp",
+ "vsock_camera_metadata.cpp",
+ "vsock_camera_server.cpp",
+ "vsock_frame_provider.cpp",
+ "cached_stream_buffer.cpp",
+ "stream_buffer_cache.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.camera.common@1.0",
+ "android.hardware.camera.device@1.0",
+ "android.hardware.camera.device@3.2",
+ "android.hardware.camera.device@3.3",
+ "android.hardware.camera.device@3.4",
+ "android.hardware.camera.device@3.5",
+ "android.hardware.camera.provider@2.4",
+ "android.hardware.camera.provider@2.5",
+ "android.hardware.camera.provider@2.6",
+ "android.hardware.camera.provider@2.4-external",
+ "android.hardware.camera.provider@2.4-legacy",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "camera.device@1.0-impl",
+ "camera.device@3.2-impl",
+ "camera.device@3.3-impl",
+ "camera.device@3.4-impl",
+ "libcamera_metadata",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "libvsock_utils",
+ "libcuttlefish_fs",
+ "libjsoncpp",
+ "libyuv",
+ "libsync",
+ "libfmq",
+ "libgralloctypes",
+ ],
+ header_libs: [
+ "camera.device@3.4-external-impl_headers",
+ "camera.device@3.4-impl_headers",
+ "camera.device@3.5-external-impl_headers",
+ "camera.device@3.5-impl_headers",
+ ],
+ static_libs: [
+ "android.hardware.camera.common@1.0-helper",
+ ],
+ include_dirs: ["device/google/cuttlefish"],
+ export_include_dirs: ["."],
+}
diff --git a/guest/hals/camera/android.hardware.camera.provider@2.6-external-vsock-service.rc b/guest/hals/camera/android.hardware.camera.provider@2.6-external-vsock-service.rc
new file mode 100644
index 0000000..72c324b
--- /dev/null
+++ b/guest/hals/camera/android.hardware.camera.provider@2.6-external-vsock-service.rc
@@ -0,0 +1,10 @@
+service vendor.camera-provider-2-6-ext /vendor/bin/hw/android.hardware.camera.provider@2.6-external-vsock-service
+ interface android.hardware.camera.provider@2.4::ICameraProvider external/0
+ interface android.hardware.camera.provider@2.5::ICameraProvider external/0
+ interface android.hardware.camera.provider@2.6::ICameraProvider external/0
+ class hal
+ user cameraserver
+ group audio camera input drmrpc
+ ioprio rt 4
+ capabilities SYS_NICE
+ task_profiles CameraServiceCapacity MaxPerformance
\ No newline at end of file
diff --git a/guest/hals/camera/cached_stream_buffer.cpp b/guest/hals/camera/cached_stream_buffer.cpp
new file mode 100644
index 0000000..ff692c7
--- /dev/null
+++ b/guest/hals/camera/cached_stream_buffer.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 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 "CachedStreamBuffer"
+#include "cached_stream_buffer.h"
+#include <hardware/gralloc.h>
+#include <log/log.h>
+#include <sync/sync.h>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+namespace {
+HandleImporter g_importer;
+}
+
+ReleaseFence::ReleaseFence(int fence_fd) : handle_(nullptr) {
+ if (fence_fd >= 0) {
+ handle_ = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
+ handle_->data[0] = fence_fd;
+ }
+}
+
+ReleaseFence::~ReleaseFence() {
+ if (handle_ != nullptr) {
+ native_handle_close(handle_);
+ native_handle_delete(handle_);
+ }
+}
+
+CachedStreamBuffer::CachedStreamBuffer()
+ : buffer_(nullptr), buffer_id_(0), stream_id_(0), acquire_fence_(-1) {}
+
+CachedStreamBuffer::CachedStreamBuffer(const StreamBuffer& buffer)
+ : buffer_(buffer.buffer.getNativeHandle()),
+ buffer_id_(buffer.bufferId),
+ stream_id_(buffer.streamId),
+ acquire_fence_(-1) {
+ g_importer.importBuffer(buffer_);
+ g_importer.importFence(buffer.acquireFence, acquire_fence_);
+}
+
+CachedStreamBuffer::CachedStreamBuffer(CachedStreamBuffer&& from) noexcept {
+ buffer_ = from.buffer_;
+ buffer_id_ = from.buffer_id_;
+ stream_id_ = from.stream_id_;
+ acquire_fence_ = from.acquire_fence_;
+ from.acquire_fence_ = -1;
+ from.buffer_ = nullptr;
+}
+
+CachedStreamBuffer& CachedStreamBuffer::operator=(
+ CachedStreamBuffer&& from) noexcept {
+ if (this != &from) {
+ buffer_ = from.buffer_;
+ buffer_id_ = from.buffer_id_;
+ stream_id_ = from.stream_id_;
+ acquire_fence_ = from.acquire_fence_;
+ from.acquire_fence_ = -1;
+ from.buffer_ = nullptr;
+ }
+ return *this;
+}
+
+CachedStreamBuffer::~CachedStreamBuffer() {
+ if (buffer_ != nullptr) {
+ g_importer.freeBuffer(buffer_);
+ }
+ g_importer.closeFence(acquire_fence_);
+}
+
+void CachedStreamBuffer::importFence(const native_handle_t* fence_handle) {
+ g_importer.closeFence(acquire_fence_);
+ g_importer.importFence(fence_handle, acquire_fence_);
+}
+
+YCbCrLayout CachedStreamBuffer::acquireAsYUV(int32_t width, int32_t height,
+ int timeout_ms) {
+ if (acquire_fence_ >= 0) {
+ if (sync_wait(acquire_fence_, timeout_ms)) {
+ ALOGW("%s: timeout while waiting acquire fence", __FUNCTION__);
+ return {};
+ } else {
+ ::close(acquire_fence_);
+ acquire_fence_ = -1;
+ }
+ }
+ IMapper::Rect region{0, 0, width, height};
+ return g_importer.lockYCbCr(buffer_, GRALLOC_USAGE_SW_WRITE_OFTEN, region);
+}
+
+void* CachedStreamBuffer::acquireAsBlob(int32_t size, int timeout_ms) {
+ if (acquire_fence_ >= 0) {
+ if (sync_wait(acquire_fence_, timeout_ms)) {
+ return nullptr;
+ } else {
+ ::close(acquire_fence_);
+ acquire_fence_ = -1;
+ }
+ }
+ return g_importer.lock(buffer_, GRALLOC_USAGE_SW_WRITE_OFTEN, size);
+}
+
+int CachedStreamBuffer::release() { return g_importer.unlock(buffer_); }
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/cached_stream_buffer.h b/guest/hals/camera/cached_stream_buffer.h
new file mode 100644
index 0000000..09a4047
--- /dev/null
+++ b/guest/hals/camera/cached_stream_buffer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 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/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include "HandleImporter.h"
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::device::V3_2::StreamBuffer;
+
+// Small wrapper for allocating/freeing native handles
+class ReleaseFence {
+ public:
+ ReleaseFence(int fence_fd);
+ ~ReleaseFence();
+
+ native_handle_t* handle() const { return handle_; }
+
+ private:
+ native_handle_t* handle_;
+};
+
+// CachedStreamBuffer holds a buffer of camera3 stream.
+class CachedStreamBuffer {
+ public:
+ CachedStreamBuffer();
+ CachedStreamBuffer(const StreamBuffer& buffer);
+ // Not copyable
+ CachedStreamBuffer(const CachedStreamBuffer&) = delete;
+ CachedStreamBuffer& operator=(const CachedStreamBuffer&) = delete;
+ // ...but movable
+ CachedStreamBuffer(CachedStreamBuffer&& from) noexcept;
+ CachedStreamBuffer& operator=(CachedStreamBuffer&& from) noexcept;
+
+ ~CachedStreamBuffer();
+
+ bool valid() const { return buffer_ != nullptr; }
+ uint64_t bufferId() const { return buffer_id_; }
+ int32_t streamId() const { return stream_id_; }
+ int acquireFence() const { return acquire_fence_; }
+
+ void importFence(const native_handle_t* fence_handle);
+ // Acquire methods wait first on acquire fence and then return pointers to
+ // data. Data is nullptr if the wait timed out
+ YCbCrLayout acquireAsYUV(int32_t width, int32_t height, int timeout_ms);
+ void* acquireAsBlob(int32_t size, int timeout_ms);
+ int release();
+
+ private:
+ buffer_handle_t buffer_;
+ uint64_t buffer_id_;
+ int32_t stream_id_;
+ int acquire_fence_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/external-service.cpp b/guest/hals/camera/external-service.cpp
new file mode 100644
index 0000000..bab6371
--- /dev/null
+++ b/guest/hals/camera/external-service.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 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 "android.hardware.camera.provider@2.6-external-service"
+
+#include <android/hardware/camera/provider/2.6/ICameraProvider.h>
+#include <hidl/LegacySupport.h>
+
+#include <binder/ProcessState.h>
+
+using android::hardware::defaultPassthroughServiceImplementation;
+using android::hardware::camera::provider::V2_6::ICameraProvider;
+
+int main() {
+ ALOGI("External camera provider service is starting.");
+ // The camera HAL may communicate to other vendor components via
+ // /dev/vndbinder
+ android::ProcessState::initWithDriver("/dev/vndbinder");
+ return defaultPassthroughServiceImplementation<ICameraProvider>(
+ "external/0", /*maxThreads*/ 6);
+}
diff --git a/guest/hals/camera/manifest.xml b/guest/hals/camera/manifest.xml
new file mode 100644
index 0000000..a798060
--- /dev/null
+++ b/guest/hals/camera/manifest.xml
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.camera.provider</name>
+ <transport>hwbinder</transport>
+ <version>2.6</version>
+ <interface>
+ <name>ICameraProvider</name>
+ <instance>external/0</instance>
+ </interface>
+ </hal>
+</manifest>
\ No newline at end of file
diff --git a/guest/hals/camera/stream_buffer_cache.cpp b/guest/hals/camera/stream_buffer_cache.cpp
new file mode 100644
index 0000000..dfa13cb
--- /dev/null
+++ b/guest/hals/camera/stream_buffer_cache.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 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 "stream_buffer_cache.h"
+#include <algorithm>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+std::shared_ptr<CachedStreamBuffer> StreamBufferCache::get(uint64_t buffer_id) {
+ auto id_match =
+ [buffer_id](const std::shared_ptr<CachedStreamBuffer>& buffer) {
+ return buffer->bufferId() == buffer_id;
+ };
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto found = std::find_if(cache_.begin(), cache_.end(), id_match);
+ return (found != cache_.end()) ? *found : nullptr;
+}
+
+void StreamBufferCache::remove(uint64_t buffer_id) {
+ auto id_match =
+ [&buffer_id](const std::shared_ptr<CachedStreamBuffer>& buffer) {
+ return buffer->bufferId() == buffer_id;
+ };
+ std::lock_guard<std::mutex> lock(mutex_);
+ cache_.erase(std::remove_if(cache_.begin(), cache_.end(), id_match));
+}
+
+void StreamBufferCache::update(const StreamBuffer& buffer) {
+ auto id = buffer.bufferId;
+ auto id_match = [id](const std::shared_ptr<CachedStreamBuffer>& buffer) {
+ return buffer->bufferId() == id;
+ };
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto found = std::find_if(cache_.begin(), cache_.end(), id_match);
+ if (found == cache_.end()) {
+ cache_.emplace_back(std::make_shared<CachedStreamBuffer>(buffer));
+ } else {
+ (*found)->importFence(buffer.acquireFence);
+ }
+}
+
+void StreamBufferCache::clear() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ cache_.clear();
+}
+
+void StreamBufferCache::removeStreamsExcept(std::set<int32_t> streams_to_keep) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ for (auto it = cache_.begin(); it != cache_.end();) {
+ if (streams_to_keep.count((*it)->streamId()) == 0) {
+ it = cache_.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/stream_buffer_cache.h b/guest/hals/camera/stream_buffer_cache.h
new file mode 100644
index 0000000..af57bc4
--- /dev/null
+++ b/guest/hals/camera/stream_buffer_cache.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <mutex>
+#include <set>
+#include <vector>
+#include "cached_stream_buffer.h"
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+class StreamBufferCache {
+ public:
+ std::shared_ptr<CachedStreamBuffer> get(uint64_t buffer_id);
+ void remove(uint64_t buffer_id);
+ void update(const StreamBuffer& buffer);
+ void clear();
+ void removeStreamsExcept(std::set<int32_t> streams_to_keep = {});
+
+ private:
+ std::mutex mutex_;
+ std::vector<std::shared_ptr<CachedStreamBuffer>> cache_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_device_3_4.cpp b/guest/hals/camera/vsock_camera_device_3_4.cpp
new file mode 100644
index 0000000..310a9f8
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_device_3_4.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 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 "VsockCameraDevice"
+//#define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include <algorithm>
+#include <array>
+#include "CameraMetadata.h"
+#include "android-base/macros.h"
+#include "include/convert.h"
+#include "vsock_camera_device_3_4.h"
+#include "vsock_camera_device_session_3_4.h"
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+VsockCameraDevice::VsockCameraDevice(
+ const std::string& id, const Settings& settings,
+ std::shared_ptr<cuttlefish::VsockConnection> connection)
+ : id_(id),
+ metadata_(settings.width, settings.height, settings.frame_rate),
+ connection_(connection),
+ is_open_(false) {
+ ALOGI("%s", __FUNCTION__);
+}
+
+VsockCameraDevice::~VsockCameraDevice() { ALOGI("%s", __FUNCTION__); }
+
+Return<void> VsockCameraDevice::getResourceCost(
+ ICameraDevice::getResourceCost_cb _hidl_cb) {
+ CameraResourceCost resCost;
+ resCost.resourceCost = 100;
+ _hidl_cb(Status::OK, resCost);
+ return Void();
+}
+
+Return<void> VsockCameraDevice::getCameraCharacteristics(
+ ICameraDevice::getCameraCharacteristics_cb _hidl_cb) {
+ V3_2::CameraMetadata hidl_vec;
+ const camera_metadata_t* metadata_ptr = metadata_.getAndLock();
+ V3_2::implementation::convertToHidl(metadata_ptr, &hidl_vec);
+ _hidl_cb(Status::OK, hidl_vec);
+ metadata_.unlock(metadata_ptr);
+ return Void();
+}
+
+Return<Status> VsockCameraDevice::setTorchMode(TorchMode) {
+ return Status::OPERATION_NOT_SUPPORTED;
+}
+
+Return<void> VsockCameraDevice::open(const sp<ICameraDeviceCallback>& callback,
+ ICameraDevice::open_cb _hidl_cb) {
+ if (callback == nullptr) {
+ ALOGE("%s: cannot open camera %s. callback is null!", __FUNCTION__,
+ id_.c_str());
+ _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+ return Void();
+ }
+
+ bool was_open = is_open_.exchange(true);
+
+ if (was_open) {
+ ALOGE("%s: cannot open an already opened camera!", __FUNCTION__);
+ _hidl_cb(Status::CAMERA_IN_USE, nullptr);
+ return Void();
+ }
+ ALOGI("%s: Initializing device for camera %s", __FUNCTION__, id_.c_str());
+ frame_provider_ = std::make_shared<cuttlefish::VsockFrameProvider>();
+ frame_provider_->start(connection_, metadata_.getPreferredWidth(),
+ metadata_.getPreferredHeight());
+ session_ = new VsockCameraDeviceSession(metadata_, frame_provider_, callback);
+ _hidl_cb(Status::OK, session_);
+ return Void();
+}
+
+Return<void> VsockCameraDevice::dumpState(
+ const ::android::hardware::hidl_handle& handle) {
+ if (handle.getNativeHandle() == nullptr) {
+ ALOGE("%s: handle must not be null", __FUNCTION__);
+ return Void();
+ }
+ if (handle->numFds != 1 || handle->numInts != 0) {
+ ALOGE("%s: handle must contain 1 FD and 0 integers! Got %d FDs and %d ints",
+ __FUNCTION__, handle->numFds, handle->numInts);
+ return Void();
+ }
+ int fd = handle->data[0];
+ dprintf(fd, "Camera:%s\n", id_.c_str());
+ return Void();
+}
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_device_3_4.h b/guest/hals/camera/vsock_camera_device_3_4.h
new file mode 100644
index 0000000..9d92991
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_device_3_4.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 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 "CameraMetadata.h"
+
+#include <android/hardware/camera/device/3.2/ICameraDevice.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "vsock_camera_device_session_3_4.h"
+#include "vsock_camera_metadata.h"
+#include "vsock_connection.h"
+#include "vsock_frame_provider.h"
+
+#include <vector>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+using namespace ::android::hardware::camera::device;
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::camera::common::V1_0::CameraResourceCost;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::TorchMode;
+using ::android::hardware::camera::device::V3_2::ICameraDevice;
+using ::android::hardware::camera::device::V3_2::ICameraDeviceCallback;
+
+class VsockCameraDevice : public ICameraDevice {
+ public:
+ using Settings = struct {
+ int32_t width;
+ int32_t height;
+ double frame_rate;
+ };
+
+ VsockCameraDevice(const std::string& id, const Settings& settings,
+ std::shared_ptr<cuttlefish::VsockConnection> connection);
+ virtual ~VsockCameraDevice();
+
+ /* Methods from ::android::hardware::camera::device::V3_2::ICameraDevice
+ * follow. */
+ Return<void> getResourceCost(ICameraDevice::getResourceCost_cb _hidl_cb);
+ Return<void> getCameraCharacteristics(
+ ICameraDevice::getCameraCharacteristics_cb _hidl_cb);
+ Return<Status> setTorchMode(TorchMode);
+ Return<void> open(const sp<ICameraDeviceCallback>&, ICameraDevice::open_cb);
+ Return<void> dumpState(const ::android::hardware::hidl_handle&);
+ /* End of Methods from
+ * ::android::hardware::camera::device::V3_2::ICameraDevice */
+
+ private:
+ std::string id_;
+ VsockCameraMetadata metadata_;
+ std::shared_ptr<cuttlefish::VsockConnection> connection_;
+ std::shared_ptr<cuttlefish::VsockFrameProvider> frame_provider_;
+ std::atomic<bool> is_open_;
+ sp<VsockCameraDeviceSession> session_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_device_session_3_4.cpp b/guest/hals/camera/vsock_camera_device_session_3_4.cpp
new file mode 100644
index 0000000..9c0b597
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_device_session_3_4.cpp
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2021 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 "VsockCameraDeviceSession"
+#include "vsock_camera_device_session_3_4.h"
+#include <hidl/Status.h>
+#include <include/convert.h>
+#include <inttypes.h>
+#include <libyuv.h>
+#include <log/log.h>
+#include "vsock_camera_metadata.h"
+
+// Partially copied from ExternalCameraDeviceSession
+namespace android::hardware::camera::device::V3_4::implementation {
+
+VsockCameraDeviceSession::VsockCameraDeviceSession(
+ VsockCameraMetadata camera_characteristics,
+ std::shared_ptr<cuttlefish::VsockFrameProvider> frame_provider,
+ const sp<ICameraDeviceCallback>& callback)
+ : camera_characteristics_(camera_characteristics),
+ frame_provider_(frame_provider),
+ callback_(callback) {
+ static constexpr size_t kMsgQueueSize = 256 * 1024;
+ request_queue_ =
+ std::make_unique<MessageQueue<uint8_t, kSynchronizedReadWrite>>(
+ kMsgQueueSize, false);
+ result_queue_ =
+ std::make_shared<MessageQueue<uint8_t, kSynchronizedReadWrite>>(
+ kMsgQueueSize, false);
+ unsigned int timeout_ms = 1000 / camera_characteristics.getPreferredFps();
+ process_requests_ = true;
+ request_processor_ =
+ std::thread([this, timeout_ms] { processRequestLoop(timeout_ms); });
+}
+
+VsockCameraDeviceSession::~VsockCameraDeviceSession() { close(); }
+
+Return<void> VsockCameraDeviceSession::constructDefaultRequestSettings(
+ V3_2::RequestTemplate type,
+ V3_2::ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) {
+ auto frame_rate = camera_characteristics_.getPreferredFps();
+ auto metadata = VsockCameraRequestMetadata(frame_rate, type);
+ V3_2::CameraMetadata hidl_metadata;
+ Status status = metadata.isValid() ? Status::OK : Status::ILLEGAL_ARGUMENT;
+ if (metadata.isValid()) {
+ camera_metadata_t* metadata_ptr = metadata.release();
+ hidl_metadata.setToExternal((uint8_t*)metadata_ptr,
+ get_camera_metadata_size(metadata_ptr));
+ }
+ _hidl_cb(status, hidl_metadata);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::getCaptureRequestMetadataQueue(
+ ICameraDeviceSession::getCaptureRequestMetadataQueue_cb _hidl_cb) {
+ _hidl_cb(*request_queue_->getDesc());
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::getCaptureResultMetadataQueue(
+ ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) {
+ _hidl_cb(*result_queue_->getDesc());
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::configureStreams(
+ const V3_2::StreamConfiguration& streams,
+ ICameraDeviceSession::configureStreams_cb _hidl_cb) {
+ // common configureStreams operate with v3_2 config and v3_3
+ // streams so we need to "downcast" v3_3 streams to v3_2 streams
+ V3_2::HalStreamConfiguration out_v32;
+ V3_3::HalStreamConfiguration out_v33;
+
+ Status status = configureStreams(streams, &out_v33);
+ size_t size = out_v33.streams.size();
+ out_v32.streams.resize(size);
+ for (size_t i = 0; i < size; i++) {
+ out_v32.streams[i] = out_v33.streams[i].v3_2;
+ }
+ _hidl_cb(status, out_v32);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::configureStreams_3_3(
+ const V3_2::StreamConfiguration& streams,
+ ICameraDeviceSession::configureStreams_3_3_cb _hidl_cb) {
+ V3_3::HalStreamConfiguration out_v33;
+ Status status = configureStreams(streams, &out_v33);
+ _hidl_cb(status, out_v33);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::configureStreams_3_4(
+ const V3_4::StreamConfiguration& requestedConfiguration,
+ ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb) {
+ // common configureStreams operate with v3_2 config and v3_3
+ // streams so we need to "downcast" v3_4 config to v3_2 and
+ // "upcast" v3_3 streams to v3_4 streams
+ V3_2::StreamConfiguration config_v32;
+ V3_3::HalStreamConfiguration out_v33;
+ V3_4::HalStreamConfiguration out_v34;
+
+ config_v32.operationMode = requestedConfiguration.operationMode;
+ config_v32.streams.resize(requestedConfiguration.streams.size());
+ for (size_t i = 0; i < config_v32.streams.size(); i++) {
+ config_v32.streams[i] = requestedConfiguration.streams[i].v3_2;
+ }
+ max_blob_size_ = getBlobSize(requestedConfiguration);
+ Status status = configureStreams(config_v32, &out_v33);
+
+ out_v34.streams.resize(out_v33.streams.size());
+ for (size_t i = 0; i < out_v34.streams.size(); i++) {
+ out_v34.streams[i].v3_3 = out_v33.streams[i];
+ }
+ _hidl_cb(status, out_v34);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::processCaptureRequest(
+ const hidl_vec<CaptureRequest>& requests,
+ const hidl_vec<BufferCache>& cachesToRemove,
+ ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) {
+ updateBufferCaches(cachesToRemove);
+
+ uint32_t count;
+ Status s = Status::OK;
+ for (count = 0; count < requests.size(); count++) {
+ s = processOneCaptureRequest(requests[count]);
+ if (s != Status::OK) {
+ break;
+ }
+ }
+
+ _hidl_cb(s, count);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::processCaptureRequest_3_4(
+ const hidl_vec<V3_4::CaptureRequest>& requests,
+ const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+ ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) {
+ updateBufferCaches(cachesToRemove);
+
+ uint32_t count;
+ Status s = Status::OK;
+ for (count = 0; count < requests.size(); count++) {
+ s = processOneCaptureRequest(requests[count].v3_2);
+ if (s != Status::OK) {
+ break;
+ }
+ }
+
+ _hidl_cb(s, count);
+ return Void();
+}
+
+Return<Status> VsockCameraDeviceSession::flush() {
+ auto timeout = std::chrono::seconds(1);
+ std::unique_lock<std::mutex> lock(request_mutex_);
+ flushing_requests_ = true;
+ auto is_empty = [this] { return pending_requests_.empty(); };
+ if (!queue_empty_.wait_for(lock, timeout, is_empty)) {
+ ALOGE("Flush timeout - %zu pending requests", pending_requests_.size());
+ }
+ flushing_requests_ = false;
+ return Status::OK;
+}
+
+Return<void> VsockCameraDeviceSession::close() {
+ process_requests_ = false;
+ if (request_processor_.joinable()) {
+ request_processor_.join();
+ }
+ frame_provider_->stop();
+ buffer_cache_.clear();
+ ALOGI("%s", __FUNCTION__);
+ return Void();
+}
+
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+Status VsockCameraDeviceSession::configureStreams(
+ const V3_2::StreamConfiguration& config,
+ V3_3::HalStreamConfiguration* out) {
+ Status status = isStreamConfigurationSupported(config);
+ if (status != Status::OK) {
+ return status;
+ }
+ updateStreamInfo(config);
+ out->streams.resize(config.streams.size());
+ for (size_t i = 0; i < config.streams.size(); i++) {
+ out->streams[i].overrideDataSpace = config.streams[i].dataSpace;
+ out->streams[i].v3_2.id = config.streams[i].id;
+ out->streams[i].v3_2.producerUsage =
+ config.streams[i].usage | BufferUsage::CPU_WRITE_OFTEN;
+ out->streams[i].v3_2.consumerUsage = 0;
+ out->streams[i].v3_2.maxBuffers = 2;
+ out->streams[i].v3_2.overrideFormat =
+ config.streams[i].format == PixelFormat::IMPLEMENTATION_DEFINED
+ ? PixelFormat::YCBCR_420_888
+ : config.streams[i].format;
+ }
+ return Status::OK;
+}
+
+using ::android::hardware::camera::device::V3_2::StreamRotation;
+using ::android::hardware::camera::device::V3_2::StreamType;
+Status VsockCameraDeviceSession::isStreamConfigurationSupported(
+ const V3_2::StreamConfiguration& config) {
+ camera_metadata_entry device_supported_streams = camera_characteristics_.find(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ int32_t stall_stream_count = 0;
+ int32_t stream_count = 0;
+ for (const auto& stream : config.streams) {
+ if (stream.rotation != StreamRotation::ROTATION_0) {
+ ALOGE("Unsupported rotation enum value %d", stream.rotation);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ if (stream.streamType == StreamType::INPUT) {
+ ALOGE("Input stream not supported");
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ bool is_supported = false;
+ // check pixel format and dimensions against camera metadata
+ for (int i = 0; i + 4 <= device_supported_streams.count; i += 4) {
+ auto format =
+ static_cast<PixelFormat>(device_supported_streams.data.i32[i]);
+ int32_t width = device_supported_streams.data.i32[i + 1];
+ int32_t height = device_supported_streams.data.i32[i + 2];
+ if (stream.format == format && stream.width == width &&
+ stream.height == height) {
+ is_supported = true;
+ break;
+ }
+ }
+ if (!is_supported) {
+ ALOGE("Unsupported format %d (%dx%d)", stream.format, stream.width,
+ stream.height);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ if (stream.format == PixelFormat::BLOB) {
+ stall_stream_count++;
+ } else {
+ stream_count++;
+ }
+ }
+ camera_metadata_entry device_stream_counts =
+ camera_characteristics_.find(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS);
+ static constexpr auto stream_index = 1;
+ auto expected_stream_count = device_stream_counts.count > stream_index
+ ? device_stream_counts.data.i32[stream_index]
+ : 0;
+ if (stream_count > expected_stream_count) {
+ ALOGE("Too many processed streams (expect <= %d, got %d)",
+ expected_stream_count, stream_count);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ static constexpr auto stall_stream_index = 2;
+ expected_stream_count =
+ device_stream_counts.count > stall_stream_index
+ ? device_stream_counts.data.i32[stall_stream_index]
+ : 0;
+ if (stall_stream_count > expected_stream_count) {
+ ALOGE("Too many stall streams (expect <= %d, got %d)",
+ expected_stream_count, stall_stream_count);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ return Status::OK;
+}
+
+unsigned int VsockCameraDeviceSession::getBlobSize(
+ const V3_4::StreamConfiguration& requestedConfiguration) {
+ camera_metadata_entry jpeg_entry =
+ camera_characteristics_.find(ANDROID_JPEG_MAX_SIZE);
+ unsigned int blob_size = jpeg_entry.count > 0 ? jpeg_entry.data.i32[0] : 0;
+ for (auto& stream : requestedConfiguration.streams) {
+ if (stream.v3_2.format == PixelFormat::BLOB) {
+ if (stream.bufferSize < blob_size) {
+ blob_size = stream.bufferSize;
+ }
+ }
+ }
+ return blob_size;
+}
+
+void VsockCameraDeviceSession::updateBufferCaches(
+ const hidl_vec<BufferCache>& to_remove) {
+ for (auto& cache : to_remove) {
+ buffer_cache_.remove(cache.bufferId);
+ }
+}
+
+void VsockCameraDeviceSession::updateStreamInfo(
+ const V3_2::StreamConfiguration& config) {
+ std::set<int32_t> stream_ids;
+ for (const auto& stream : config.streams) {
+ stream_cache_[stream.id] = stream;
+ stream_ids.emplace(stream.id);
+ }
+ buffer_cache_.removeStreamsExcept(stream_ids);
+}
+
+Status VsockCameraDeviceSession::processOneCaptureRequest(
+ const CaptureRequest& request) {
+ const camera_metadata_t* request_settings = nullptr;
+ V3_2::CameraMetadata hidl_settings;
+ if (request.fmqSettingsSize > 0) {
+ if (!getRequestSettingsFmq(request.fmqSettingsSize, hidl_settings)) {
+ ALOGE("%s: Could not read capture request settings from fmq!",
+ __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ } else if (!V3_2::implementation::convertFromHidl(hidl_settings,
+ &request_settings)) {
+ ALOGE("%s: fmq request settings metadata is corrupt!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ } else if (!V3_2::implementation::convertFromHidl(request.settings,
+ &request_settings)) {
+ ALOGE("%s: request settings metadata is corrupt!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ if (request_settings != nullptr) {
+ // Update request settings. This must happen on first request
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ latest_request_settings_ = request_settings;
+ } else if (latest_request_settings_.isEmpty()) {
+ ALOGE("%s: Undefined capture request settings!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ std::vector<uint64_t> buffer_ids;
+ for (size_t i = 0; i < request.outputBuffers.size(); i++) {
+ buffer_cache_.update(request.outputBuffers[i]);
+ buffer_ids.emplace_back(request.outputBuffers[i].bufferId);
+ }
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ ReadVsockRequest request_to_process = {
+ .buffer_ids = buffer_ids,
+ .frame_number = request.frameNumber,
+ .timestamp = 0,
+ .settings = latest_request_settings_,
+ .buffer_count = static_cast<uint32_t>(buffer_ids.size())};
+ putRequestToQueue(request_to_process);
+ return Status::OK;
+}
+
+bool VsockCameraDeviceSession::getRequestSettingsFmq(
+ uint64_t size, V3_2::CameraMetadata& hidl_settings) {
+ hidl_settings.resize(size);
+ return request_queue_->read(hidl_settings.data(), size);
+}
+
+bool VsockCameraDeviceSession::getRequestFromQueue(ReadVsockRequest& req,
+ unsigned int timeout_ms) {
+ auto timeout = std::chrono::milliseconds(timeout_ms);
+ std::unique_lock<std::mutex> lock(request_mutex_);
+ auto not_empty = [this] { return !pending_requests_.empty(); };
+ if (request_available_.wait_for(lock, timeout, not_empty)) {
+ req = pending_requests_.top();
+ pending_requests_.pop();
+ return true;
+ }
+ queue_empty_.notify_one();
+ return false;
+}
+
+void VsockCameraDeviceSession::putRequestToQueue(
+ const ReadVsockRequest& request) {
+ std::lock_guard<std::mutex> lock(request_mutex_);
+ pending_requests_.push(request);
+ request_available_.notify_one();
+}
+
+void VsockCameraDeviceSession::fillCaptureResult(
+ common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp) {
+ const uint8_t af_state = ANDROID_CONTROL_AF_STATE_INACTIVE;
+ md.update(ANDROID_CONTROL_AF_STATE, &af_state, 1);
+
+ const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
+ md.update(ANDROID_CONTROL_AE_STATE, &aeState, 1);
+
+ const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
+ md.update(ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
+
+ const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED;
+ md.update(ANDROID_CONTROL_AWB_STATE, &awbState, 1);
+
+ const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+ md.update(ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
+
+ const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
+ md.update(ANDROID_FLASH_STATE, &flashState, 1);
+
+ const uint8_t requestPipelineMaxDepth = 4;
+ md.update(ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
+
+ camera_metadata_entry active_array_size =
+ camera_characteristics_.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ md.update(ANDROID_SCALER_CROP_REGION, active_array_size.data.i32, 4);
+
+ md.update(ANDROID_SENSOR_TIMESTAMP, ×tamp, 1);
+
+ const uint8_t lensShadingMapMode =
+ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+ md.update(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
+
+ const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
+ md.update(ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1);
+}
+
+using ::android::hardware::camera::device::V3_2::MsgType;
+using ::android::hardware::camera::device::V3_2::NotifyMsg;
+void VsockCameraDeviceSession::notifyShutter(uint32_t frame_number,
+ nsecs_t timestamp) {
+ NotifyMsg msg;
+ msg.type = MsgType::SHUTTER;
+ msg.msg.shutter.frameNumber = frame_number;
+ msg.msg.shutter.timestamp = timestamp;
+ callback_->notify({msg});
+}
+
+void VsockCameraDeviceSession::notifyError(uint32_t frame_number,
+ int32_t stream_id, ErrorCode code) {
+ NotifyMsg msg;
+ msg.type = MsgType::ERROR;
+ msg.msg.error.frameNumber = frame_number;
+ msg.msg.error.errorStreamId = stream_id;
+ msg.msg.error.errorCode = code;
+ callback_->notify({msg});
+}
+
+void VsockCameraDeviceSession::tryWriteFmqResult(V3_2::CaptureResult& result) {
+ result.fmqResultSize = 0;
+ if (result_queue_->availableToWrite() == 0 || result.result.size() == 0) {
+ return;
+ }
+ if (result_queue_->write(result.result.data(), result.result.size())) {
+ result.fmqResultSize = result.result.size();
+ result.result.resize(0);
+ }
+}
+
+using ::android::hardware::camera::device::V3_2::BufferStatus;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+void VsockCameraDeviceSession::processRequestLoop(
+ unsigned int wait_timeout_ms) {
+ while (process_requests_.load()) {
+ ReadVsockRequest request;
+ if (!getRequestFromQueue(request, wait_timeout_ms)) {
+ continue;
+ }
+ if (!frame_provider_->isRunning()) {
+ notifyError(request.frame_number, -1, ErrorCode::ERROR_DEVICE);
+ break;
+ }
+ frame_provider_->waitYUVFrame(wait_timeout_ms);
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (request.timestamp == 0) {
+ request.timestamp = now;
+ notifyShutter(request.frame_number, request.timestamp);
+ }
+ std::vector<ReleaseFence> release_fences;
+ std::vector<StreamBuffer> result_buffers;
+ std::vector<uint64_t> pending_buffers;
+ bool request_ok = true;
+ for (auto buffer_id : request.buffer_ids) {
+ auto buffer = buffer_cache_.get(buffer_id);
+ auto stream_id = buffer ? buffer->streamId() : -1;
+ if (!buffer || stream_cache_.count(stream_id) == 0) {
+ ALOGE("%s: Invalid buffer", __FUNCTION__);
+ notifyError(request.frame_number, -1, ErrorCode::ERROR_REQUEST);
+ request_ok = false;
+ break;
+ }
+ bool has_result = false;
+ auto stream = stream_cache_[stream_id];
+ if (flushing_requests_.load()) {
+ has_result = false;
+ release_fences.emplace_back(buffer->acquireFence());
+ } else if (stream.format == PixelFormat::YCBCR_420_888 ||
+ stream.format == PixelFormat::IMPLEMENTATION_DEFINED) {
+ auto dst_yuv =
+ buffer->acquireAsYUV(stream.width, stream.height, wait_timeout_ms);
+ has_result =
+ frame_provider_->copyYUVFrame(stream.width, stream.height, dst_yuv);
+ release_fences.emplace_back(buffer->release());
+ } else if (stream.format == PixelFormat::BLOB) {
+ auto time_elapsed = now - request.timestamp;
+ if (time_elapsed == 0) {
+ frame_provider_->requestJpeg();
+ pending_buffers.push_back(buffer_id);
+ continue;
+ } else if (frame_provider_->jpegPending()) {
+ static constexpr auto kMaxWaitNs = 2000000000L;
+ if (time_elapsed < kMaxWaitNs) {
+ pending_buffers.push_back(buffer_id);
+ continue;
+ }
+ ALOGE("%s: Blob request timed out after %" PRId64 "ms", __FUNCTION__,
+ ns2ms(time_elapsed));
+ frame_provider_->cancelJpegRequest();
+ has_result = false;
+ release_fences.emplace_back(buffer->acquireFence());
+ notifyError(request.frame_number, buffer->streamId(),
+ ErrorCode::ERROR_BUFFER);
+ } else {
+ ALOGI("%s: Blob ready - capture duration=%" PRId64 "ms", __FUNCTION__,
+ ns2ms(time_elapsed));
+ auto dst_blob =
+ buffer->acquireAsBlob(max_blob_size_, wait_timeout_ms);
+ has_result = frame_provider_->copyJpegData(max_blob_size_, dst_blob);
+ release_fences.emplace_back(buffer->release());
+ }
+ } else {
+ ALOGE("%s: Format %d not supported", __FUNCTION__, stream.format);
+ has_result = false;
+ release_fences.emplace_back(buffer->acquireFence());
+ notifyError(request.frame_number, buffer->streamId(),
+ ErrorCode::ERROR_BUFFER);
+ }
+ result_buffers.push_back(
+ {.streamId = buffer->streamId(),
+ .bufferId = buffer->bufferId(),
+ .buffer = nullptr,
+ .status = has_result ? BufferStatus::OK : BufferStatus::ERROR,
+ .releaseFence = release_fences.back().handle()});
+ }
+ if (!request_ok) {
+ continue;
+ }
+
+ V3_2::CaptureResult result;
+ bool results_filled = request.settings.exists(ANDROID_SENSOR_TIMESTAMP);
+ if (!results_filled) {
+ fillCaptureResult(request.settings, request.timestamp);
+ const camera_metadata_t* metadata = request.settings.getAndLock();
+ V3_2::implementation::convertToHidl(metadata, &result.result);
+ request.settings.unlock(metadata);
+ tryWriteFmqResult(result);
+ }
+ if (!result_buffers.empty() || !results_filled) {
+ result.frameNumber = request.frame_number;
+ result.partialResult = !results_filled ? 1 : 0;
+ result.inputBuffer.streamId = -1;
+ result.outputBuffers = result_buffers;
+ std::vector<V3_2::CaptureResult> results{result};
+ auto status = callback_->processCaptureResult(results);
+ release_fences.clear();
+ if (!status.isOk()) {
+ ALOGE("%s: processCaptureResult error: %s", __FUNCTION__,
+ status.description().c_str());
+ }
+ }
+ if (!pending_buffers.empty()) {
+ // some buffers still pending
+ request.buffer_ids = pending_buffers;
+ putRequestToQueue(request);
+ }
+ }
+}
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_device_session_3_4.h b/guest/hals/camera/vsock_camera_device_session_3_4.h
new file mode 100644
index 0000000..2bafa75
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_device_session_3_4.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2021 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/hardware/camera/device/3.2/ICameraDevice.h>
+#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <fmq/MessageQueue.h>
+#include <queue>
+#include <thread>
+#include "stream_buffer_cache.h"
+#include "vsock_camera_metadata.h"
+#include "vsock_frame_provider.h"
+
+namespace android::hardware::camera::device::V3_4::implementation {
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::device::V3_2::BufferCache;
+using ::android::hardware::camera::device::V3_2::CaptureRequest;
+using ::android::hardware::camera::device::V3_2::ErrorCode;
+using ::android::hardware::camera::device::V3_2::ICameraDeviceCallback;
+using ::android::hardware::camera::device::V3_2::RequestTemplate;
+using ::android::hardware::camera::device::V3_2::Stream;
+using ::android::hardware::camera::device::V3_4::ICameraDeviceSession;
+using ::android::hardware::camera::device::V3_4::StreamConfiguration;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+
+class VsockCameraDeviceSession : public ICameraDeviceSession {
+ public:
+ VsockCameraDeviceSession(
+ VsockCameraMetadata camera_characteristics,
+ std::shared_ptr<cuttlefish::VsockFrameProvider> frame_provider,
+ const sp<ICameraDeviceCallback>& callback);
+
+ ~VsockCameraDeviceSession();
+
+ Return<void> constructDefaultRequestSettings(
+ RequestTemplate,
+ ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb);
+
+ Return<void> configureStreams(const V3_2::StreamConfiguration&,
+ ICameraDeviceSession::configureStreams_cb);
+
+ Return<void> configureStreams_3_3(
+ const V3_2::StreamConfiguration&,
+ ICameraDeviceSession::configureStreams_3_3_cb);
+
+ Return<void> configureStreams_3_4(
+ const V3_4::StreamConfiguration& requestedConfiguration,
+ ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb);
+
+ Return<void> getCaptureRequestMetadataQueue(
+ ICameraDeviceSession::getCaptureRequestMetadataQueue_cb);
+
+ Return<void> getCaptureResultMetadataQueue(
+ ICameraDeviceSession::getCaptureResultMetadataQueue_cb);
+
+ Return<void> processCaptureRequest(
+ const hidl_vec<CaptureRequest>&, const hidl_vec<BufferCache>&,
+ ICameraDeviceSession::processCaptureRequest_cb);
+
+ Return<Status> flush();
+ Return<void> close();
+
+ Return<void> processCaptureRequest_3_4(
+ const hidl_vec<V3_4::CaptureRequest>& requests,
+ const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+ ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb);
+
+ private:
+ struct ReadVsockRequest {
+ std::vector<uint64_t> buffer_ids;
+ uint32_t frame_number;
+ nsecs_t timestamp;
+ common::V1_0::helper::CameraMetadata settings;
+ uint32_t buffer_count;
+ };
+ struct VsockRequestComparator {
+ bool operator()(const ReadVsockRequest& lhs, const ReadVsockRequest& rhs) {
+ return lhs.frame_number > rhs.frame_number;
+ }
+ };
+ void updateBufferCaches(const hidl_vec<BufferCache>& to_remove);
+ Status configureStreams(const V3_2::StreamConfiguration& config,
+ V3_3::HalStreamConfiguration* out);
+ unsigned int getBlobSize(
+ const V3_4::StreamConfiguration& requestedConfiguration);
+ Status isStreamConfigurationSupported(
+ const V3_2::StreamConfiguration& config);
+ void updateStreamInfo(const V3_2::StreamConfiguration& config);
+ Status processOneCaptureRequest(const CaptureRequest& request);
+ bool getRequestSettingsFmq(uint64_t size,
+ V3_2::CameraMetadata& hidl_settings);
+ void processRequestLoop(unsigned int timeout);
+ bool getRequestFromQueue(ReadVsockRequest& request, unsigned int timeout_ms);
+ void putRequestToQueue(const ReadVsockRequest& request);
+ void fillCaptureResult(common::V1_0::helper::CameraMetadata& md,
+ nsecs_t timestamp);
+ void notifyShutter(uint32_t frame_number, nsecs_t timestamp);
+ void notifyError(uint32_t frame_number, int32_t stream_id, ErrorCode code);
+ void tryWriteFmqResult(V3_2::CaptureResult& result);
+ VsockCameraMetadata camera_characteristics_;
+ std::shared_ptr<cuttlefish::VsockFrameProvider> frame_provider_;
+ const sp<ICameraDeviceCallback> callback_;
+ std::unique_ptr<MessageQueue<uint8_t, kSynchronizedReadWrite>> request_queue_;
+ std::shared_ptr<MessageQueue<uint8_t, kSynchronizedReadWrite>> result_queue_;
+ std::mutex settings_mutex_;
+ common::V1_0::helper::CameraMetadata latest_request_settings_;
+
+ StreamBufferCache buffer_cache_;
+ std::map<int32_t, Stream> stream_cache_;
+
+ std::mutex request_mutex_;
+ std::condition_variable request_available_;
+ std::condition_variable queue_empty_;
+ std::priority_queue<ReadVsockRequest, std::vector<ReadVsockRequest>,
+ VsockRequestComparator>
+ pending_requests_;
+ std::thread request_processor_;
+ std::atomic<bool> process_requests_;
+ std::atomic<bool> flushing_requests_;
+
+ unsigned int max_blob_size_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_metadata.cpp b/guest/hals/camera/vsock_camera_metadata.cpp
new file mode 100644
index 0000000..2e92970
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_metadata.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2021 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 "vsock_camera_metadata.h"
+
+#include <hardware/camera3.h>
+#include <utils/misc.h>
+#include <vector>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+namespace {
+// Mostly copied from ExternalCameraDevice
+const uint8_t kHardwarelevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
+const uint8_t kAberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
+const uint8_t kAvailableAberrationModes[] = {
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF};
+const int32_t kExposureCompensation = 0;
+const uint8_t kAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+const int32_t kControlMaxRegions[] = {/*AE*/ 0, /*AWB*/ 0, /*AF*/ 0};
+const uint8_t kVideoStabilizationMode =
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+const uint8_t kAwbAvailableMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+const uint8_t kAePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
+const uint8_t kAeAvailableMode = ANDROID_CONTROL_AE_MODE_ON;
+const uint8_t kAvailableFffect = ANDROID_CONTROL_EFFECT_MODE_OFF;
+const uint8_t kControlMode = ANDROID_CONTROL_MODE_AUTO;
+const uint8_t kControlAvailableModes[] = {ANDROID_CONTROL_MODE_OFF,
+ ANDROID_CONTROL_MODE_AUTO};
+const uint8_t kEdgeMode = ANDROID_EDGE_MODE_OFF;
+const uint8_t kFlashInfo = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
+const uint8_t kFlashMode = ANDROID_FLASH_MODE_OFF;
+const uint8_t kHotPixelMode = ANDROID_HOT_PIXEL_MODE_OFF;
+const uint8_t kJpegQuality = 90;
+const int32_t kJpegOrientation = 0;
+const int32_t kThumbnailSize[] = {240, 180};
+const int32_t kJpegAvailableThumbnailSizes[] = {0, 0, 240, 180};
+const uint8_t kFocusDistanceCalibration =
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED;
+const uint8_t kOpticalStabilizationMode =
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+const uint8_t kFacing = ANDROID_LENS_FACING_EXTERNAL;
+const float kLensMinFocusDistance = 0.0f;
+const uint8_t kNoiseReductionMode = ANDROID_NOISE_REDUCTION_MODE_OFF;
+const int32_t kPartialResultCount = 1;
+const uint8_t kRequestPipelineMaxDepth = 4;
+const int32_t kRequestMaxNumInputStreams = 0;
+const float kScalerAvailableMaxDigitalZoom[] = {1};
+const uint8_t kCroppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
+const int32_t kTestPatternMode = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
+const int32_t kTestPatternModes[] = {ANDROID_SENSOR_TEST_PATTERN_MODE_OFF};
+const uint8_t kTimestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
+const int32_t kOrientation = 0;
+const uint8_t kAvailableShadingMode = ANDROID_SHADING_MODE_OFF;
+const uint8_t kFaceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+const int32_t kMaxFaceCount = 0;
+const uint8_t kAvailableHotpixelMode =
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF;
+const uint8_t kLensShadingMapMode =
+ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+const int32_t kMaxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
+const int32_t kControlAeCompensationRange[] = {0, 0};
+const camera_metadata_rational_t kControlAeCompensationStep[] = {{0, 1}};
+const uint8_t kAfTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
+const uint8_t kAfMode = ANDROID_CONTROL_AF_MODE_OFF;
+const uint8_t kAfAvailableModes[] = {ANDROID_CONTROL_AF_MODE_OFF};
+const uint8_t kAvailableSceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED;
+const uint8_t kAeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
+const uint8_t kAwbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
+const int32_t kHalFormats[] = {HAL_PIXEL_FORMAT_BLOB,
+ HAL_PIXEL_FORMAT_YCbCr_420_888,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED};
+const int32_t kRequestMaxNumOutputStreams[] = {
+ /*RAW*/ 0,
+ /*Processed*/ 2,
+ /*Stall*/ 1};
+const uint8_t kAvailableCapabilities[] = {
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE};
+const int32_t kAvailableRequestKeys[] = {
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+ ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+ ANDROID_CONTROL_AE_LOCK,
+ ANDROID_CONTROL_AE_MODE,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ ANDROID_CONTROL_AF_MODE,
+ ANDROID_CONTROL_AF_TRIGGER,
+ ANDROID_CONTROL_AWB_LOCK,
+ ANDROID_CONTROL_AWB_MODE,
+ ANDROID_CONTROL_CAPTURE_INTENT,
+ ANDROID_CONTROL_EFFECT_MODE,
+ ANDROID_CONTROL_MODE,
+ ANDROID_CONTROL_SCENE_MODE,
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+ ANDROID_FLASH_MODE,
+ ANDROID_JPEG_ORIENTATION,
+ ANDROID_JPEG_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_SIZE,
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+ ANDROID_NOISE_REDUCTION_MODE,
+ ANDROID_SCALER_CROP_REGION,
+ ANDROID_SENSOR_TEST_PATTERN_MODE,
+ ANDROID_STATISTICS_FACE_DETECT_MODE,
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE};
+const int32_t kAvailableResultKeys[] = {
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+ ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+ ANDROID_CONTROL_AE_LOCK,
+ ANDROID_CONTROL_AE_MODE,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+ ANDROID_CONTROL_AE_STATE,
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ ANDROID_CONTROL_AF_MODE,
+ ANDROID_CONTROL_AF_STATE,
+ ANDROID_CONTROL_AF_TRIGGER,
+ ANDROID_CONTROL_AWB_LOCK,
+ ANDROID_CONTROL_AWB_MODE,
+ ANDROID_CONTROL_AWB_STATE,
+ ANDROID_CONTROL_CAPTURE_INTENT,
+ ANDROID_CONTROL_EFFECT_MODE,
+ ANDROID_CONTROL_MODE,
+ ANDROID_CONTROL_SCENE_MODE,
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+ ANDROID_FLASH_MODE,
+ ANDROID_FLASH_STATE,
+ ANDROID_JPEG_ORIENTATION,
+ ANDROID_JPEG_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_SIZE,
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+ ANDROID_NOISE_REDUCTION_MODE,
+ ANDROID_REQUEST_PIPELINE_DEPTH,
+ ANDROID_SCALER_CROP_REGION,
+ ANDROID_SENSOR_TIMESTAMP,
+ ANDROID_STATISTICS_FACE_DETECT_MODE,
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
+ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
+ ANDROID_STATISTICS_SCENE_FLICKER};
+const int32_t kAvailableCharacteristicsKeys[] = {
+ ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+ ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+ ANDROID_CONTROL_AE_COMPENSATION_STEP,
+ ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+ ANDROID_CONTROL_AF_AVAILABLE_MODES,
+ ANDROID_CONTROL_AVAILABLE_EFFECTS,
+ ANDROID_CONTROL_AVAILABLE_MODES,
+ ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+ ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+ ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+ ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+ ANDROID_CONTROL_MAX_REGIONS,
+ ANDROID_FLASH_INFO_AVAILABLE,
+ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+ ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+ ANDROID_LENS_FACING,
+ ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+ ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+ ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+ ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
+ ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+ ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+ ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
+ ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ ANDROID_SCALER_CROPPING_TYPE,
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+ ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
+ ANDROID_SENSOR_ORIENTATION,
+ ANDROID_SHADING_AVAILABLE_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+ ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+ ANDROID_SYNC_MAX_LATENCY};
+const std::map<RequestTemplate, uint8_t> kTemplateToIntent = {
+ {RequestTemplate::PREVIEW, ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW},
+ {RequestTemplate::STILL_CAPTURE,
+ ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE},
+ {RequestTemplate::VIDEO_RECORD,
+ ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD},
+ {RequestTemplate::VIDEO_SNAPSHOT,
+ ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT},
+};
+} // namespace
+
+// Constructor sets the default characteristics for vsock camera
+VsockCameraMetadata::VsockCameraMetadata(int32_t width, int32_t height,
+ int32_t fps)
+ : width_(width), height_(height), fps_(fps) {
+ update(ANDROID_CONTROL_AE_COMPENSATION_RANGE, kControlAeCompensationRange,
+ NELEM(kControlAeCompensationRange));
+ update(ANDROID_CONTROL_AE_COMPENSATION_STEP, kControlAeCompensationStep,
+ NELEM(kControlAeCompensationStep));
+ update(ANDROID_CONTROL_AF_AVAILABLE_MODES, kAfAvailableModes,
+ NELEM(kAfAvailableModes));
+ update(ANDROID_CONTROL_AVAILABLE_SCENE_MODES, &kAvailableSceneMode, 1);
+ update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &kAeLockAvailable, 1);
+ update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &kAwbLockAvailable, 1);
+ update(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+ kScalerAvailableMaxDigitalZoom, NELEM(kScalerAvailableMaxDigitalZoom));
+ update(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, kAvailableCapabilities,
+ NELEM(kAvailableCapabilities));
+ update(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &kHardwarelevel, 1);
+ update(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+ kAvailableAberrationModes, NELEM(kAvailableAberrationModes));
+ update(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, &kAntibandingMode, 1);
+ update(ANDROID_CONTROL_MAX_REGIONS, kControlMaxRegions,
+ NELEM(kControlMaxRegions));
+ update(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+ &kVideoStabilizationMode, 1);
+ update(ANDROID_CONTROL_AWB_AVAILABLE_MODES, &kAwbAvailableMode, 1);
+ update(ANDROID_CONTROL_AE_AVAILABLE_MODES, &kAeAvailableMode, 1);
+ update(ANDROID_CONTROL_AVAILABLE_EFFECTS, &kAvailableFffect, 1);
+ update(ANDROID_CONTROL_AVAILABLE_MODES, kControlAvailableModes,
+ NELEM(kControlAvailableModes));
+ update(ANDROID_EDGE_AVAILABLE_EDGE_MODES, &kEdgeMode, 1);
+ update(ANDROID_FLASH_INFO_AVAILABLE, &kFlashInfo, 1);
+ update(ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES, &kHotPixelMode, 1);
+ update(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, kJpegAvailableThumbnailSizes,
+ NELEM(kJpegAvailableThumbnailSizes));
+ update(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+ &kFocusDistanceCalibration, 1);
+ update(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, &kLensMinFocusDistance, 1);
+ update(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+ &kOpticalStabilizationMode, 1);
+ update(ANDROID_LENS_FACING, &kFacing, 1);
+ update(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+ &kNoiseReductionMode, 1);
+ update(ANDROID_NOISE_REDUCTION_MODE, &kNoiseReductionMode, 1);
+ update(ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &kPartialResultCount, 1);
+ update(ANDROID_REQUEST_PIPELINE_MAX_DEPTH, &kRequestPipelineMaxDepth, 1);
+ update(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, kRequestMaxNumOutputStreams,
+ NELEM(kRequestMaxNumOutputStreams));
+ update(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, &kRequestMaxNumInputStreams, 1);
+ update(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+ kScalerAvailableMaxDigitalZoom, NELEM(kScalerAvailableMaxDigitalZoom));
+ update(ANDROID_SCALER_CROPPING_TYPE, &kCroppingType, 1);
+ update(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES, kTestPatternModes,
+ NELEM(kTestPatternModes));
+ update(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, &kTimestampSource, 1);
+ update(ANDROID_SENSOR_ORIENTATION, &kOrientation, 1);
+ update(ANDROID_SHADING_AVAILABLE_MODES, &kAvailableShadingMode, 1);
+ update(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, &kFaceDetectMode,
+ 1);
+ update(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, &kMaxFaceCount, 1);
+ update(ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+ &kAvailableHotpixelMode, 1);
+ update(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+ &kLensShadingMapMode, 1);
+ update(ANDROID_SYNC_MAX_LATENCY, &kMaxLatency, 1);
+ update(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, kAvailableRequestKeys,
+ NELEM(kAvailableRequestKeys));
+ update(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, kAvailableResultKeys,
+ NELEM(kAvailableResultKeys));
+ update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+ kAvailableCharacteristicsKeys, NELEM(kAvailableCharacteristicsKeys));
+
+ // assume max 2bytes/pixel + info because client might provide us pngs
+ const int32_t jpeg_max_size = width * height * 2 + sizeof(camera3_jpeg_blob);
+ update(ANDROID_JPEG_MAX_SIZE, &jpeg_max_size, 1);
+
+ std::vector<int64_t> min_frame_durations;
+ std::vector<int32_t> stream_configurations;
+ std::vector<int64_t> stall_durations;
+
+ int64_t frame_duration = 1000000000L / fps;
+ for (const auto& format : kHalFormats) {
+ stream_configurations.push_back(format);
+ min_frame_durations.push_back(format);
+ stall_durations.push_back(format);
+ stream_configurations.push_back(width);
+ min_frame_durations.push_back(width);
+ stall_durations.push_back(width);
+ stream_configurations.push_back(height);
+ min_frame_durations.push_back(height);
+ stall_durations.push_back(height);
+ stream_configurations.push_back(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
+ min_frame_durations.push_back(frame_duration);
+ stall_durations.push_back((format == HAL_PIXEL_FORMAT_BLOB) ? 2000000000L
+ : 0);
+ }
+ update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ stream_configurations.data(), stream_configurations.size());
+ update(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+ min_frame_durations.data(), min_frame_durations.size());
+ update(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, stall_durations.data(),
+ stall_durations.size());
+
+ int32_t active_array_size[] = {0, 0, width, height};
+ update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ active_array_size, NELEM(active_array_size));
+ update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, active_array_size,
+ NELEM(active_array_size));
+
+ int32_t pixel_array_size[] = {width, height};
+ update(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, pixel_array_size,
+ NELEM(pixel_array_size));
+
+ int32_t max_frame_rate = fps;
+ int32_t min_frame_rate = max_frame_rate / 2;
+ int32_t frame_rates[] = {min_frame_rate, max_frame_rate};
+ update(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, frame_rates,
+ NELEM(frame_rates));
+ int64_t max_frame_duration = 1000000000L / min_frame_rate;
+ update(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, &max_frame_duration, 1);
+}
+
+VsockCameraRequestMetadata::VsockCameraRequestMetadata(int32_t fps,
+ RequestTemplate type) {
+ update(ANDROID_COLOR_CORRECTION_ABERRATION_MODE, &kAberrationMode, 1);
+ update(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, &kExposureCompensation, 1);
+ update(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, &kVideoStabilizationMode, 1);
+ update(ANDROID_CONTROL_AWB_MODE, &kAwbAvailableMode, 1);
+ update(ANDROID_CONTROL_AE_MODE, &kAeAvailableMode, 1);
+ update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, &kAePrecaptureTrigger, 1);
+ update(ANDROID_CONTROL_AF_MODE, &kAfMode, 1);
+ update(ANDROID_CONTROL_AF_TRIGGER, &kAfTrigger, 1);
+ update(ANDROID_CONTROL_SCENE_MODE, &kAvailableSceneMode, 1);
+ update(ANDROID_CONTROL_EFFECT_MODE, &kAvailableFffect, 1);
+ update(ANDROID_FLASH_MODE, &kFlashMode, 1);
+ update(ANDROID_JPEG_THUMBNAIL_SIZE, kThumbnailSize, NELEM(kThumbnailSize));
+ update(ANDROID_JPEG_QUALITY, &kJpegQuality, 1);
+ update(ANDROID_JPEG_THUMBNAIL_QUALITY, &kJpegQuality, 1);
+ update(ANDROID_JPEG_ORIENTATION, &kJpegOrientation, 1);
+ update(ANDROID_LENS_OPTICAL_STABILIZATION_MODE, &kOpticalStabilizationMode,
+ 1);
+ update(ANDROID_NOISE_REDUCTION_MODE, &kNoiseReductionMode, 1);
+ update(ANDROID_SENSOR_TEST_PATTERN_MODE, &kTestPatternMode, 1);
+ update(ANDROID_STATISTICS_FACE_DETECT_MODE, &kFaceDetectMode, 1);
+ update(ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE, &kAvailableHotpixelMode, 1);
+
+ int32_t max_frame_rate = fps;
+ int32_t min_frame_rate = max_frame_rate / 2;
+ int32_t frame_rates[] = {min_frame_rate, max_frame_rate};
+ update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE, frame_rates, NELEM(frame_rates));
+
+ update(ANDROID_CONTROL_AE_ANTIBANDING_MODE, &kAntibandingMode, 1);
+ update(ANDROID_CONTROL_MODE, &kControlMode, 1);
+
+ auto it = kTemplateToIntent.find(type);
+ if (it != kTemplateToIntent.end()) {
+ auto intent = it->second;
+ update(ANDROID_CONTROL_CAPTURE_INTENT, &intent, 1);
+ is_valid_ = true;
+ } else {
+ is_valid_ = false;
+ }
+}
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_metadata.h b/guest/hals/camera/vsock_camera_metadata.h
new file mode 100644
index 0000000..d5b43c7
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_metadata.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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 <CameraMetadata.h>
+#include <android/hardware/camera/device/3.2/ICameraDevice.h>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+// Small wrappers for mostly hard-coded camera metadata
+// Some parameters are calculated from remote camera frame size and fps
+using ::android::hardware::camera::common::V1_0::helper::CameraMetadata;
+class VsockCameraMetadata : public CameraMetadata {
+ public:
+ VsockCameraMetadata(int32_t width, int32_t height, int32_t fps);
+
+ int32_t getPreferredWidth() const { return width_; }
+ int32_t getPreferredHeight() const { return height_; }
+ int32_t getPreferredFps() const { return fps_; }
+
+ private:
+ int32_t width_;
+ int32_t height_;
+ int32_t fps_;
+};
+
+using ::android::hardware::camera::device::V3_2::RequestTemplate;
+class VsockCameraRequestMetadata : public CameraMetadata {
+ public:
+ VsockCameraRequestMetadata(int32_t fps, RequestTemplate type);
+ // Tells whether the metadata has been successfully constructed
+ // from the parameters
+ bool isValid() const { return is_valid_; }
+
+ private:
+ bool is_valid_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_provider_2_6.cpp b/guest/hals/camera/vsock_camera_provider_2_6.cpp
new file mode 100644
index 0000000..e39e1d3
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_provider_2_6.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2021 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 "VsockCameraProvider"
+#include "vsock_camera_provider_2_6.h"
+#include <cutils/properties.h>
+#include <log/log.h>
+#include "vsock_camera_server.h"
+
+namespace android::hardware::camera::provider::V2_6::implementation {
+
+namespace {
+VsockCameraServer gCameraServer;
+constexpr auto kDeviceName = "device@3.4/external/0";
+} // namespace
+
+using android::hardware::camera::provider::V2_6::ICameraProvider;
+extern "C" ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name) {
+ return (strcmp(name, "external/0") == 0)
+ ? new VsockCameraProvider(&gCameraServer)
+ : nullptr;
+}
+
+VsockCameraProvider::VsockCameraProvider(VsockCameraServer* server) {
+ server_ = server;
+ if (!server->isRunning()) {
+ constexpr static const auto camera_port_property =
+ "ro.boot.vsock_camera_port";
+ constexpr static const auto camera_cid_property =
+ "ro.boot.vsock_camera_cid";
+ auto port = property_get_int32(camera_port_property, -1);
+ auto cid = property_get_int32(camera_cid_property, -1);
+ if (port > 0) {
+ server->start(port, cid);
+ }
+ }
+}
+
+VsockCameraProvider::~VsockCameraProvider() {
+ server_->setConnectedCallback(nullptr);
+}
+
+Return<Status> VsockCameraProvider::setCallback(
+ const sp<ICameraProviderCallback>& callback) {
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ callbacks_ = callback;
+ }
+ server_->setConnectedCallback(
+ [this](std::shared_ptr<cuttlefish::VsockConnection> connection,
+ VsockCameraDevice::Settings settings) {
+ connection_ = connection;
+ settings_ = settings;
+ deviceAdded(kDeviceName);
+ connection_->SetDisconnectCallback(
+ [this] { deviceRemoved(kDeviceName); });
+ });
+ return Status::OK;
+}
+
+Return<void> VsockCameraProvider::getVendorTags(
+ ICameraProvider::getVendorTags_cb _hidl_cb) {
+ // No vendor tag support
+ hidl_vec<VendorTagSection> empty;
+ _hidl_cb(Status::OK, empty);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::getCameraIdList(
+ ICameraProvider::getCameraIdList_cb _hidl_cb) {
+ // External camera HAL always report 0 camera, and extra cameras
+ // are just reported via cameraDeviceStatusChange callbacks
+ hidl_vec<hidl_string> empty;
+ _hidl_cb(Status::OK, empty);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::isSetTorchModeSupported(
+ ICameraProvider::isSetTorchModeSupported_cb _hidl_cb) {
+ // setTorchMode API is supported, though right now no external camera device
+ // has a flash unit.
+ _hidl_cb(Status::OK, true);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::getCameraDeviceInterface_V1_x(
+ const hidl_string&,
+ ICameraProvider::getCameraDeviceInterface_V1_x_cb _hidl_cb) {
+ // External Camera HAL does not support HAL1
+ _hidl_cb(Status::OPERATION_NOT_SUPPORTED, nullptr);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::getCameraDeviceInterface_V3_x(
+ const hidl_string& name_hidl_str,
+ ICameraProvider::getCameraDeviceInterface_V3_x_cb _hidl_cb) {
+ std::string name(name_hidl_str.c_str());
+ if (name != kDeviceName) {
+ _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+ return Void();
+ }
+
+ _hidl_cb(Status::OK, new VsockCameraDevice(name, settings_, connection_));
+ return Void();
+}
+
+Return<void> VsockCameraProvider::notifyDeviceStateChange(
+ hardware::hidl_bitfield<DeviceState> /*newState*/) {
+ return Void();
+}
+
+Return<void> VsockCameraProvider::getConcurrentStreamingCameraIds(
+ getConcurrentStreamingCameraIds_cb _hidl_cb) {
+ hidl_vec<hidl_vec<hidl_string>> hidl_camera_id_combinations;
+ _hidl_cb(Status::OK, hidl_camera_id_combinations);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::isConcurrentStreamCombinationSupported(
+ const hidl_vec<CameraIdAndStreamCombination>&,
+ isConcurrentStreamCombinationSupported_cb _hidl_cb) {
+ _hidl_cb(Status::OK, false);
+ return Void();
+}
+
+void VsockCameraProvider::deviceAdded(const char* name) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (callbacks_ != nullptr) {
+ callbacks_->cameraDeviceStatusChange(name, CameraDeviceStatus::PRESENT);
+ }
+}
+
+void VsockCameraProvider::deviceRemoved(const char* name) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (callbacks_ != nullptr) {
+ callbacks_->cameraDeviceStatusChange(name, CameraDeviceStatus::NOT_PRESENT);
+ }
+}
+
+} // namespace android::hardware::camera::provider::V2_6::implementation
diff --git a/guest/hals/camera/vsock_camera_provider_2_6.h b/guest/hals/camera/vsock_camera_provider_2_6.h
new file mode 100644
index 0000000..67b64f5
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_provider_2_6.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 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 <mutex>
+
+#include <android/hardware/camera/provider/2.6/ICameraProvider.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <json/json.h>
+
+#include "vsock_camera_device_3_4.h"
+#include "vsock_camera_server.h"
+#include "vsock_connection.h"
+
+namespace android::hardware::camera::provider::V2_6::implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hardware::camera::common::V1_0::CameraDeviceStatus;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::VendorTagSection;
+using ::android::hardware::camera::device::V3_4::implementation::
+ VsockCameraDevice;
+using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
+using ::android::hardware::camera::provider::V2_5::DeviceState;
+using ::android::hardware::camera::provider::V2_6::CameraIdAndStreamCombination;
+using ::android::hardware::camera::provider::V2_6::ICameraProvider;
+
+class VsockCameraProvider : public ICameraProvider {
+ public:
+ VsockCameraProvider(VsockCameraServer* server);
+ ~VsockCameraProvider();
+
+ Return<Status> setCallback(
+ const sp<ICameraProviderCallback>& callback) override;
+ Return<void> getVendorTags(getVendorTags_cb _hidl_cb) override;
+ Return<void> getCameraIdList(getCameraIdList_cb _hidl_cb) override;
+ Return<void> isSetTorchModeSupported(
+ isSetTorchModeSupported_cb _hidl_cb) override;
+ Return<void> getCameraDeviceInterface_V1_x(
+ const hidl_string& cameraDeviceName,
+ getCameraDeviceInterface_V1_x_cb _hidl_cb) override;
+ Return<void> getCameraDeviceInterface_V3_x(
+ const hidl_string& cameraDeviceName,
+ getCameraDeviceInterface_V3_x_cb _hidl_cb) override;
+ Return<void> notifyDeviceStateChange(
+ hardware::hidl_bitfield<DeviceState> newState) override;
+ Return<void> getConcurrentStreamingCameraIds(
+ getConcurrentStreamingCameraIds_cb _hidl_cb) override;
+ Return<void> isConcurrentStreamCombinationSupported(
+ const hidl_vec<CameraIdAndStreamCombination>& configs,
+ isConcurrentStreamCombinationSupported_cb _hidl_cb) override;
+
+ private:
+ void deviceRemoved(const char* name);
+ void deviceAdded(const char* name);
+ std::mutex mutex_;
+ sp<ICameraProviderCallback> callbacks_;
+ std::shared_ptr<cuttlefish::VsockConnection> connection_;
+ VsockCameraDevice::Settings settings_;
+ VsockCameraServer* server_;
+};
+
+} // namespace android::hardware::camera::provider::V2_6::implementation
diff --git a/guest/hals/camera/vsock_camera_server.cpp b/guest/hals/camera/vsock_camera_server.cpp
new file mode 100644
index 0000000..381a985
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_server.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 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 "VsockCameraServer"
+#include "vsock_camera_server.h"
+#include <log/log.h>
+
+namespace android::hardware::camera::provider::V2_6::implementation {
+
+using ::android::hardware::camera::device::V3_4::implementation::
+ VsockCameraDevice;
+namespace {
+
+bool containsValidSettings(const VsockCameraDevice::Settings& settings) {
+ return settings.width > 0 && settings.height > 0 && settings.frame_rate > 0.0;
+}
+
+bool readSettingsFromJson(VsockCameraDevice::Settings& settings,
+ const Json::Value& json) {
+ VsockCameraDevice::Settings new_settings;
+ new_settings.width = json["width"].asInt();
+ new_settings.height = json["height"].asInt();
+ new_settings.frame_rate = json["frame_rate"].asDouble();
+ if (containsValidSettings(new_settings)) {
+ settings = new_settings;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace
+
+VsockCameraServer::VsockCameraServer() {
+ ALOGI("%s: Create server", __FUNCTION__);
+ connection_ = std::make_shared<cuttlefish::VsockServerConnection>();
+}
+
+VsockCameraServer::~VsockCameraServer() {
+ ALOGI("%s: Destroy server", __FUNCTION__);
+ stop();
+}
+
+void VsockCameraServer::start(unsigned int port, unsigned int cid) {
+ stop();
+ is_running_ = true;
+ server_thread_ = std::thread([this, port, cid] { serverLoop(port, cid); });
+}
+
+void VsockCameraServer::stop() {
+ connection_->ServerShutdown();
+ is_running_ = false;
+ if (server_thread_.joinable()) {
+ server_thread_.join();
+ }
+}
+
+void VsockCameraServer::setConnectedCallback(callback_t callback) {
+ connected_callback_ = callback;
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ if (callback && connection_->IsConnected() &&
+ containsValidSettings(settings_)) {
+ callback(connection_, settings_);
+ }
+}
+
+void VsockCameraServer::serverLoop(unsigned int port, unsigned int cid) {
+ while (is_running_.load()) {
+ ALOGI("%s: Accepting connections...", __FUNCTION__);
+ if (connection_->Connect(port, cid)) {
+ auto json_settings = connection_->ReadJsonMessage();
+ VsockCameraDevice::Settings settings;
+ if (readSettingsFromJson(settings, json_settings)) {
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ settings_ = settings;
+ if (connected_callback_) {
+ connected_callback_(connection_, settings);
+ }
+ ALOGI("%s: Client connected", __FUNCTION__);
+ } else {
+ ALOGE("%s: Could not read settings", __FUNCTION__);
+ }
+ } else {
+ ALOGE("%s: Accepting connections failed", __FUNCTION__);
+ }
+ }
+ ALOGI("%s: Exiting", __FUNCTION__);
+}
+
+} // namespace android::hardware::camera::provider::V2_6::implementation
diff --git a/guest/hals/camera/vsock_camera_server.h b/guest/hals/camera/vsock_camera_server.h
new file mode 100644
index 0000000..5cc3b38
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_server.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 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 <atomic>
+#include <thread>
+#include "vsock_camera_device_3_4.h"
+#include "vsock_connection.h"
+
+namespace android::hardware::camera::provider::V2_6::implementation {
+
+using ::android::hardware::camera::device::V3_4::implementation::
+ VsockCameraDevice;
+
+class VsockCameraServer {
+ public:
+ VsockCameraServer();
+ ~VsockCameraServer();
+
+ VsockCameraServer(const VsockCameraServer&) = delete;
+ VsockCameraServer& operator=(const VsockCameraServer&) = delete;
+
+ bool isRunning() const { return is_running_.load(); }
+
+ void start(unsigned int port, unsigned int cid);
+ void stop();
+
+ using callback_t =
+ std::function<void(std::shared_ptr<cuttlefish::VsockConnection>,
+ VsockCameraDevice::Settings)>;
+ void setConnectedCallback(callback_t callback);
+
+ private:
+ void serverLoop(unsigned int port, unsigned int cid);
+ std::thread server_thread_;
+ std::atomic<bool> is_running_;
+ std::shared_ptr<cuttlefish::VsockServerConnection> connection_;
+ std::mutex settings_mutex_;
+ VsockCameraDevice::Settings settings_;
+ callback_t connected_callback_;
+};
+
+} // namespace android::hardware::camera::provider::V2_6::implementation
diff --git a/guest/hals/camera/vsock_frame_provider.cpp b/guest/hals/camera/vsock_frame_provider.cpp
new file mode 100644
index 0000000..435d5f9
--- /dev/null
+++ b/guest/hals/camera/vsock_frame_provider.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 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 "vsock_frame_provider.h"
+#include <hardware/camera3.h>
+#include <libyuv.h>
+#include <cstring>
+#define LOG_TAG "VsockFrameProvider"
+#include <log/log.h>
+
+namespace cuttlefish {
+
+VsockFrameProvider::~VsockFrameProvider() { stop(); }
+
+void VsockFrameProvider::start(
+ std::shared_ptr<cuttlefish::VsockConnection> connection, uint32_t width,
+ uint32_t height) {
+ stop();
+ running_ = true;
+ connection_ = connection;
+ reader_thread_ =
+ std::thread([this, width, height] { VsockReadLoop(width, height); });
+}
+
+void VsockFrameProvider::stop() {
+ running_ = false;
+ jpeg_pending_ = false;
+ if (reader_thread_.joinable()) {
+ reader_thread_.join();
+ }
+ connection_ = nullptr;
+}
+
+bool VsockFrameProvider::waitYUVFrame(unsigned int max_wait_ms) {
+ auto timeout = std::chrono::milliseconds(max_wait_ms);
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ std::unique_lock<std::mutex> lock(frame_mutex_);
+ return yuv_frame_updated_.wait_for(
+ lock, timeout, [this, now] { return timestamp_.load() > now; });
+}
+
+void VsockFrameProvider::requestJpeg() {
+ jpeg_pending_ = true;
+ Json::Value message;
+ message["event"] = "VIRTUAL_DEVICE_CAPTURE_IMAGE";
+ if (connection_) {
+ connection_->WriteMessage(message);
+ }
+}
+
+void VsockFrameProvider::cancelJpegRequest() { jpeg_pending_ = false; }
+
+bool VsockFrameProvider::copyYUVFrame(uint32_t w, uint32_t h, YCbCrLayout dst) {
+ size_t y_size = w * h;
+ size_t cbcr_size = (w / 2) * (h / 2);
+ size_t total_size = y_size + 2 * cbcr_size;
+ std::lock_guard<std::mutex> lock(frame_mutex_);
+ if (frame_.size() < total_size) {
+ ALOGE("%s: %zu is too little for %ux%u frame", __FUNCTION__, frame_.size(),
+ w, h);
+ return false;
+ }
+ if (dst.y == nullptr) {
+ ALOGE("%s: Destination is nullptr!", __FUNCTION__);
+ return false;
+ }
+ YCbCrLayout src{.y = static_cast<void*>(frame_.data()),
+ .cb = static_cast<void*>(frame_.data() + y_size),
+ .cr = static_cast<void*>(frame_.data() + y_size + cbcr_size),
+ .yStride = w,
+ .cStride = w / 2,
+ .chromaStep = 1};
+ uint8_t* src_y = static_cast<uint8_t*>(src.y);
+ uint8_t* dst_y = static_cast<uint8_t*>(dst.y);
+ uint8_t* src_cb = static_cast<uint8_t*>(src.cb);
+ uint8_t* dst_cb = static_cast<uint8_t*>(dst.cb);
+ uint8_t* src_cr = static_cast<uint8_t*>(src.cr);
+ uint8_t* dst_cr = static_cast<uint8_t*>(dst.cr);
+ libyuv::CopyPlane(src_y, src.yStride, dst_y, dst.yStride, w, h);
+ if (dst.chromaStep == 1) {
+ // Planar
+ libyuv::CopyPlane(src_cb, src.cStride, dst_cb, dst.cStride, w / 2, h / 2);
+ libyuv::CopyPlane(src_cr, src.cStride, dst_cr, dst.cStride, w / 2, h / 2);
+ } else if (dst.chromaStep == 2 && dst_cr - dst_cb == 1) {
+ // Interleaved cb/cr planes starting with cb
+ libyuv::MergeUVPlane(src_cb, src.cStride, src_cr, src.cStride, dst_cb,
+ dst.cStride, w / 2, h / 2);
+ } else if (dst.chromaStep == 2 && dst_cb - dst_cr == 1) {
+ // Interleaved cb/cr planes starting with cr
+ libyuv::MergeUVPlane(src_cr, src.cStride, src_cb, src.cStride, dst_cr,
+ dst.cStride, w / 2, h / 2);
+ } else {
+ ALOGE("%s: Unsupported interleaved U/V layout", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+bool VsockFrameProvider::copyJpegData(uint32_t size, void* dst) {
+ std::lock_guard<std::mutex> lock(jpeg_mutex_);
+ auto jpeg_header_offset = size - sizeof(struct camera3_jpeg_blob);
+ if (cached_jpeg_.empty()) {
+ ALOGE("%s: No source data", __FUNCTION__);
+ return false;
+ } else if (dst == nullptr) {
+ ALOGE("%s: Destination is nullptr", __FUNCTION__);
+ return false;
+ } else if (jpeg_header_offset <= cached_jpeg_.size()) {
+ ALOGE("%s: %ubyte target buffer too small", __FUNCTION__, size);
+ return false;
+ }
+ std::memcpy(dst, cached_jpeg_.data(), cached_jpeg_.size());
+ struct camera3_jpeg_blob* blob = reinterpret_cast<struct camera3_jpeg_blob*>(
+ static_cast<char*>(dst) + jpeg_header_offset);
+ blob->jpeg_blob_id = CAMERA3_JPEG_BLOB_ID;
+ blob->jpeg_size = cached_jpeg_.size();
+ cached_jpeg_.clear();
+ return true;
+}
+
+bool VsockFrameProvider::isBlob(const std::vector<char>& blob) {
+ bool is_png = blob.size() > 4 && (blob[0] & 0xff) == 0x89 &&
+ (blob[1] & 0xff) == 0x50 && (blob[2] & 0xff) == 0x4e &&
+ (blob[3] & 0xff) == 0x47;
+ bool is_jpeg =
+ blob.size() > 2 && (blob[0] & 0xff) == 0xff && (blob[1] & 0xff) == 0xd8;
+ return is_png || is_jpeg;
+}
+
+bool VsockFrameProvider::framesizeMatches(uint32_t width, uint32_t height,
+ const std::vector<char>& data) {
+ return data.size() == 3 * width * height / 2;
+}
+
+void VsockFrameProvider::VsockReadLoop(uint32_t width, uint32_t height) {
+ jpeg_pending_ = false;
+ while (running_.load() && connection_->ReadMessage(next_frame_)) {
+ if (framesizeMatches(width, height, next_frame_)) {
+ std::lock_guard<std::mutex> lock(frame_mutex_);
+ timestamp_ = systemTime();
+ frame_.swap(next_frame_);
+ yuv_frame_updated_.notify_one();
+ } else if (isBlob(next_frame_)) {
+ std::lock_guard<std::mutex> lock(jpeg_mutex_);
+ bool was_pending = jpeg_pending_.exchange(false);
+ if (was_pending) {
+ cached_jpeg_.swap(next_frame_);
+ }
+ } else {
+ ALOGE("%s: Unexpected data of %zu bytes", __FUNCTION__,
+ next_frame_.size());
+ }
+ }
+ if (!connection_->IsConnected()) {
+ ALOGE("%s: Connection closed - exiting", __FUNCTION__);
+ running_ = false;
+ }
+}
+
+} // namespace cuttlefish
diff --git a/guest/hals/camera/vsock_frame_provider.h b/guest/hals/camera/vsock_frame_provider.h
new file mode 100644
index 0000000..4454d3e
--- /dev/null
+++ b/guest/hals/camera/vsock_frame_provider.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 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/hardware/graphics/mapper/2.0/IMapper.h>
+#include <atomic>
+#include <mutex>
+#include <thread>
+#include <vector>
+#include "utils/Timers.h"
+#include "vsock_connection.h"
+
+namespace cuttlefish {
+
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+
+// VsockFrameProvider reads data from vsock
+// Users can get the data by using copyYUVFrame/copyJpegData methods
+class VsockFrameProvider {
+ public:
+ VsockFrameProvider() = default;
+ ~VsockFrameProvider();
+
+ VsockFrameProvider(const VsockFrameProvider&) = delete;
+ VsockFrameProvider& operator=(const VsockFrameProvider&) = delete;
+
+ void start(std::shared_ptr<cuttlefish::VsockConnection> connection,
+ uint32_t expected_width, uint32_t expected_height);
+ void stop();
+ void requestJpeg();
+ void cancelJpegRequest();
+ bool jpegPending() const { return jpeg_pending_.load(); }
+ bool isRunning() const { return running_.load(); }
+ bool waitYUVFrame(unsigned int max_wait_ms);
+ bool copyYUVFrame(uint32_t width, uint32_t height, YCbCrLayout dst);
+ bool copyJpegData(uint32_t size, void* dst);
+
+ private:
+ bool isBlob(const std::vector<char>& blob);
+ bool framesizeMatches(uint32_t width, uint32_t height,
+ const std::vector<char>& data);
+ void VsockReadLoop(uint32_t expected_width, uint32_t expected_height);
+ std::thread reader_thread_;
+ std::mutex frame_mutex_;
+ std::mutex jpeg_mutex_;
+ std::atomic<nsecs_t> timestamp_;
+ std::atomic<bool> running_;
+ std::atomic<bool> jpeg_pending_;
+ std::vector<char> frame_;
+ std::vector<char> next_frame_;
+ std::vector<char> cached_jpeg_;
+ std::condition_variable yuv_frame_updated_;
+ std::shared_ptr<cuttlefish::VsockConnection> connection_;
+};
+
+} // namespace cuttlefish
diff --git a/guest/hals/confirmationui/.clang-format b/guest/hals/confirmationui/.clang-format
new file mode 100644
index 0000000..b0dc94c
--- /dev/null
+++ b/guest/hals/confirmationui/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+IndentCaseLabels: false
+ColumnLimit: 100
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
diff --git a/guest/hals/confirmationui/Android.bp b/guest/hals/confirmationui/Android.bp
new file mode 100644
index 0000000..8ee78a0
--- /dev/null
+++ b/guest/hals/confirmationui/Android.bp
@@ -0,0 +1,86 @@
+// Copyright (C) 2021 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK. Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+ name: "android.hardware.confirmationui@1.0-service.cuttlefish",
+ defaults: ["hidl_defaults", "cuttlefish_guest_only"],
+ relative_install_path: "hw",
+ vendor: true,
+ shared_libs: [
+ "android.hardware.confirmationui@1.0",
+ "android.hardware.confirmationui.not-so-secure-input",
+ "android.hardware.confirmationui@1.0-lib.cuttlefish",
+ "libbase",
+ "libhidlbase",
+ "libutils",
+ ],
+
+ init_rc: ["android.hardware.confirmationui@1.0-service.cuttlefish.rc"],
+
+ vintf_fragments: ["android.hardware.confirmationui@1.0-service.cuttlefish.xml"],
+
+ srcs: [
+ "service.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-DTEEUI_USE_STD_VECTOR",
+ ],
+}
+
+cc_library {
+ name: "android.hardware.confirmationui@1.0-lib.cuttlefish",
+ defaults: ["hidl_defaults", "cuttlefish_guest_only"],
+ vendor: true,
+ shared_libs: [
+ "android.hardware.confirmationui@1.0",
+ "android.hardware.keymaster@4.0",
+ "libbase",
+ "libdmabufheap",
+ "libhidlbase",
+ "libteeui_hal_support",
+ "libtrusty",
+ "libutils",
+ ],
+
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "TrustyApp.cpp",
+ "TrustyConfirmationUI.cpp",
+ ],
+ static_libs: [
+ "libcuttlefish_fs",
+ "libcuttlefish_confui",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-DTEEUI_USE_STD_VECTOR",
+ ],
+}
+
diff --git a/guest/hals/confirmationui/README b/guest/hals/confirmationui/README
new file mode 100644
index 0000000..45d4e76
--- /dev/null
+++ b/guest/hals/confirmationui/README
@@ -0,0 +1,20 @@
+## Secure UI Architecture
+
+To implement confirmationui a secure UI architecture is required. This entails a way
+to display the confirmation dialog driven by a reduced trusted computing base, typically
+a trusted execution environment (TEE), without having to rely on Linux and the Android
+system for integrity and authenticity of input events. This implementation provides
+neither. But it provides most of the functionlity required to run a full Android Protected
+Confirmation feature when integrated into a secure UI architecture.
+
+## Secure input (NotSoSecureInput)
+
+This implementation does not provide any security guaranties.
+The input method (NotSoSecureInput) runs a cryptographic protocols that is
+sufficiently secure IFF the end point is implemented on a trustworthy
+secure input device. But since the endpoint is currently in the HAL
+service itself this implementation is not secure.
+
+NOTE that a secure input device end point needs a good source of entropy
+for generating nonces. The current implementation (NotSoSecureInput.cpp#generateNonce)
+uses a constant nonce.
\ No newline at end of file
diff --git a/guest/hals/confirmationui/TrustyApp.cpp b/guest/hals/confirmationui/TrustyApp.cpp
new file mode 100644
index 0000000..cee8655
--- /dev/null
+++ b/guest/hals/confirmationui/TrustyApp.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2021, 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 "TrustyApp.h"
+
+#include <BufferAllocator/BufferAllocator.h>
+#include <android-base/logging.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <trusty/tipc.h>
+
+#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
+
+namespace android {
+namespace trusty {
+namespace confirmationui {
+
+using ::android::base::unique_fd;
+
+static inline uintptr_t RoundPageUp(uintptr_t val) {
+ return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+}
+
+ssize_t TrustyApp::TrustyRpc(const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+ uint8_t* iend) {
+ uint32_t olen = oend - obegin;
+
+ if (olen > shm_len_) {
+ LOG(ERROR) << AT << "request message too long to fit in shared memory";
+ return -1;
+ }
+
+ memcpy(shm_base_, obegin, olen);
+
+ confirmationui_hdr hdr = {
+ .cmd = CONFIRMATIONUI_CMD_MSG,
+ };
+ confirmationui_msg_args args = {
+ .msg_len = olen,
+ };
+ iovec iov[] = {
+ {
+ .iov_base = &hdr,
+ .iov_len = sizeof(hdr),
+ },
+ {
+ .iov_base = &args,
+ .iov_len = sizeof(args),
+ },
+ };
+
+ int rc = tipc_send(handle_, iov, countof(iov), NULL, 0);
+ if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
+ LOG(ERROR) << AT << "failed to send MSG request";
+ return -1;
+ }
+
+ rc = readv(handle_, iov, countof(iov));
+ if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
+ LOG(ERROR) << AT << "failed to receive MSG response";
+ return -1;
+ }
+
+ if (hdr.cmd != (CONFIRMATIONUI_CMD_MSG | CONFIRMATIONUI_RESP_BIT)) {
+ LOG(ERROR) << AT << "unknown response command: " << hdr.cmd;
+ return -1;
+ }
+
+ uint32_t ilen = iend - ibegin;
+ if (args.msg_len > ilen) {
+ LOG(ERROR) << AT << "response message too long to fit in return buffer";
+ return -1;
+ }
+
+ memcpy(ibegin, shm_base_, args.msg_len);
+
+ return args.msg_len;
+}
+
+TrustyApp::TrustyApp(const std::string& path, const std::string& appname)
+ : handle_(kInvalidHandle) {
+ unique_fd tipc_handle(tipc_connect(path.c_str(), appname.c_str()));
+ if (tipc_handle < 0) {
+ LOG(ERROR) << AT << "failed to connect to Trusty TA \"" << appname << "\" using dev:"
+ << "\"" << path << "\"";
+ return;
+ }
+
+ uint32_t shm_len = RoundPageUp(CONFIRMATIONUI_MAX_MSG_SIZE);
+ BufferAllocator allocator;
+ unique_fd dma_buf(allocator.Alloc("system", shm_len));
+ if (dma_buf < 0) {
+ LOG(ERROR) << AT << "failed to allocate shared memory buffer";
+ return;
+ }
+
+ confirmationui_hdr hdr = {
+ .cmd = CONFIRMATIONUI_CMD_INIT,
+ };
+ confirmationui_init_req args = {
+ .shm_len = shm_len,
+ };
+ iovec iov[] = {
+ {
+ .iov_base = &hdr,
+ .iov_len = sizeof(hdr),
+ },
+ {
+ .iov_base = &args,
+ .iov_len = sizeof(args),
+ },
+ };
+ trusty_shm shm = {
+ .fd = dma_buf,
+ .transfer = TRUSTY_SHARE,
+ };
+
+ int rc = tipc_send(tipc_handle, iov, 2, &shm, 1);
+ if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
+ LOG(ERROR) << AT << "failed to send INIT request";
+ return;
+ }
+
+ rc = read(tipc_handle, &hdr, sizeof(hdr));
+ if (rc != static_cast<int>(sizeof(hdr))) {
+ LOG(ERROR) << AT << "failed to receive INIT response";
+ return;
+ }
+
+ if (hdr.cmd != (CONFIRMATIONUI_CMD_INIT | CONFIRMATIONUI_RESP_BIT)) {
+ LOG(ERROR) << AT << "unknown response command: " << hdr.cmd;
+ return;
+ }
+
+ void* shm_base = mmap(0, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+ if (shm_base == MAP_FAILED) {
+ LOG(ERROR) << AT << "failed to mmap() shared memory buffer";
+ return;
+ }
+
+ handle_ = std::move(tipc_handle);
+ shm_base_ = shm_base;
+ shm_len_ = shm_len;
+
+ LOG(INFO) << AT << "succeeded to connect to Trusty TA \"" << appname << "\"";
+}
+
+TrustyApp::~TrustyApp() {
+ LOG(INFO) << "Done shutting down TrustyApp";
+}
+
+} // namespace confirmationui
+} // namespace trusty
+} // namespace android
diff --git a/guest/hals/confirmationui/TrustyApp.h b/guest/hals/confirmationui/TrustyApp.h
new file mode 100644
index 0000000..fb57828
--- /dev/null
+++ b/guest/hals/confirmationui/TrustyApp.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2020, 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 <TrustyIpc.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <teeui/msg_formatting.h>
+#include <trusty/tipc.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <functional>
+#include <future>
+#include <iostream>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#define AT __FILE__ ":" << __LINE__ << ": "
+
+namespace android {
+namespace trusty {
+namespace confirmationui {
+
+using ::teeui::Message;
+using ::teeui::msg2tuple_t;
+using ::teeui::ReadStream;
+using ::teeui::WriteStream;
+
+#ifndef TEEUI_USE_STD_VECTOR
+/*
+ * TEEUI_USE_STD_VECTOR makes certain wire types like teeui::MsgString and
+ * teeui::MsgVector be aliases for std::vector. This is required for thread safe
+ * message serialization. Always compile this with -DTEEUI_USE_STD_VECTOR set in
+ * CFLAGS of the HAL service.
+ */
+#error "Must be compiled with -DTEEUI_USE_STD_VECTOR."
+#endif
+
+enum class TrustyAppError : int32_t {
+ OK,
+ ERROR = -1,
+ MSG_TOO_LONG = -2,
+};
+
+class TrustyApp {
+ private:
+ android::base::unique_fd handle_;
+ void* shm_base_;
+ size_t shm_len_;
+ static constexpr const int kInvalidHandle = -1;
+ /*
+ * This mutex serializes communication with the trusted app, not handle_.
+ * Calling issueCmd during construction or deletion is undefined behavior.
+ */
+ std::mutex mutex_;
+
+ public:
+ TrustyApp(const std::string& path, const std::string& appname);
+ ~TrustyApp();
+
+ ssize_t TrustyRpc(const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin, uint8_t* iend);
+
+ template <typename Request, typename Response, typename... T>
+ std::tuple<TrustyAppError, msg2tuple_t<Response>> issueCmd(const T&... args) {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ if (handle_ == kInvalidHandle) {
+ LOG(ERROR) << "TrustyApp not connected";
+ return {TrustyAppError::ERROR, {}};
+ }
+
+ uint8_t buffer[CONFIRMATIONUI_MAX_MSG_SIZE];
+ WriteStream out(buffer);
+
+ out = write(Request(), out, args...);
+ if (!out) {
+ LOG(ERROR) << AT << "send command failed: message formatting";
+ return {TrustyAppError::MSG_TOO_LONG, {}};
+ }
+
+ auto rc = TrustyRpc(&buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+ &buffer[CONFIRMATIONUI_MAX_MSG_SIZE]);
+ if (rc < 0) return {TrustyAppError::ERROR, {}};
+
+ ReadStream in(&buffer[0], rc);
+ auto result = read(Response(), in);
+ if (!std::get<0>(result)) {
+ LOG(ERROR) << "send command failed: message parsing";
+ return {TrustyAppError::ERROR, {}};
+ }
+
+ return {std::get<0>(result) ? TrustyAppError::OK : TrustyAppError::ERROR,
+ tuple_tail(std::move(result))};
+ }
+
+ template <typename Request, typename... T> TrustyAppError issueCmd(const T&... args) {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ if (handle_ == kInvalidHandle) {
+ LOG(ERROR) << "TrustyApp not connected";
+ return TrustyAppError::ERROR;
+ }
+
+ uint8_t buffer[CONFIRMATIONUI_MAX_MSG_SIZE];
+ WriteStream out(buffer);
+
+ out = write(Request(), out, args...);
+ if (!out) {
+ LOG(ERROR) << AT << "send command failed: message formatting";
+ return TrustyAppError::MSG_TOO_LONG;
+ }
+
+ auto rc = TrustyRpc(&buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+ &buffer[CONFIRMATIONUI_MAX_MSG_SIZE]);
+ if (rc < 0) {
+ LOG(ERROR) << "send command failed: " << strerror(errno) << " (" << errno << ")";
+ return TrustyAppError::ERROR;
+ }
+
+ if (rc > 0) {
+ LOG(ERROR) << "Unexpected non zero length response";
+ return TrustyAppError::ERROR;
+ }
+ return TrustyAppError::OK;
+ }
+
+ operator bool() const { return handle_ != kInvalidHandle; }
+};
+
+} // namespace confirmationui
+} // namespace trusty
+} // namespace android
diff --git a/guest/hals/confirmationui/TrustyConfirmationUI.cpp b/guest/hals/confirmationui/TrustyConfirmationUI.cpp
new file mode 100644
index 0000000..5112438
--- /dev/null
+++ b/guest/hals/confirmationui/TrustyConfirmationUI.cpp
@@ -0,0 +1,513 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TrustyConfirmationUI.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/hardware/keymaster/4.0/types.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <poll.h>
+#include <pthread.h>
+#include <secure_input/evdev.h>
+#include <secure_input/secure_input_device.h>
+#include <secure_input/secure_input_proto.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <teeui/msg_formatting.h>
+#include <teeui/utils.h>
+#include <time.h>
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+using namespace secure_input;
+
+using ::android::trusty::confirmationui::TrustyAppError;
+
+using ::teeui::AbortMsg;
+using ::teeui::DeliverTestCommandMessage;
+using ::teeui::DeliverTestCommandResponse;
+using ::teeui::FetchConfirmationResult;
+using ::teeui::MsgString;
+using ::teeui::MsgVector;
+using ::teeui::PromptUserConfirmationMsg;
+using ::teeui::PromptUserConfirmationResponse;
+using ::teeui::ResultMsg;
+
+using ::secure_input::createSecureInput;
+
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+
+using ::std::tie;
+
+using TeeuiRc = ::teeui::ResponseCode;
+
+constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
+constexpr const char kConfirmationuiAppName[] = CONFIRMATIONUI_PORT;
+
+namespace {
+
+class Finalize {
+ private:
+ std::function<void()> f_;
+
+ public:
+ Finalize(std::function<void()> f) : f_(f) {}
+ ~Finalize() {
+ if (f_) f_();
+ }
+ void release() { f_ = {}; }
+};
+
+ResponseCode convertRc(TeeuiRc trc) {
+ static_assert(
+ uint32_t(TeeuiRc::OK) == uint32_t(ResponseCode::OK) &&
+ uint32_t(TeeuiRc::Canceled) == uint32_t(ResponseCode::Canceled) &&
+ uint32_t(TeeuiRc::Aborted) == uint32_t(ResponseCode::Aborted) &&
+ uint32_t(TeeuiRc::OperationPending) == uint32_t(ResponseCode::OperationPending) &&
+ uint32_t(TeeuiRc::Ignored) == uint32_t(ResponseCode::Ignored) &&
+ uint32_t(TeeuiRc::SystemError) == uint32_t(ResponseCode::SystemError) &&
+ uint32_t(TeeuiRc::Unimplemented) == uint32_t(ResponseCode::Unimplemented) &&
+ uint32_t(TeeuiRc::Unexpected) == uint32_t(ResponseCode::Unexpected) &&
+ uint32_t(TeeuiRc::UIError) == uint32_t(ResponseCode::UIError) &&
+ uint32_t(TeeuiRc::UIErrorMissingGlyph) == uint32_t(ResponseCode::UIErrorMissingGlyph) &&
+ uint32_t(TeeuiRc::UIErrorMessageTooLong) ==
+ uint32_t(ResponseCode::UIErrorMessageTooLong) &&
+ uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) ==
+ uint32_t(ResponseCode::UIErrorMalformedUTF8Encoding),
+ "teeui::ResponseCode and "
+ "::android::hardware::confirmationui::V1_0::Responsecude are out of "
+ "sync");
+ return ResponseCode(trc);
+}
+
+teeui::UIOption convertUIOption(UIOption uio) {
+ static_assert(uint32_t(UIOption::AccessibilityInverted) ==
+ uint32_t(teeui::UIOption::AccessibilityInverted) &&
+ uint32_t(UIOption::AccessibilityMagnified) ==
+ uint32_t(teeui::UIOption::AccessibilityMagnified),
+ "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
+ "anre out of sync");
+ return teeui::UIOption(uio);
+}
+
+inline MsgString hidl2MsgString(const hidl_string& s) {
+ return {s.c_str(), s.c_str() + s.size()};
+}
+template <typename T> inline MsgVector<T> hidl2MsgVector(const hidl_vec<T>& v) {
+ return {v};
+}
+
+inline MsgVector<teeui::UIOption> hidl2MsgVector(const hidl_vec<UIOption>& v) {
+ MsgVector<teeui::UIOption> result(v.size());
+ for (unsigned int i = 0; i < v.size(); ++i) {
+ result[i] = convertUIOption(v[i]);
+ }
+ return result;
+}
+
+} // namespace
+
+TrustyConfirmationUI::TrustyConfirmationUI()
+ : listener_state_(ListenerState::None), prompt_result_(ResponseCode::Ignored) {}
+
+TrustyConfirmationUI::~TrustyConfirmationUI() {
+ ListenerState state = listener_state_;
+ if (state == ListenerState::SetupDone || state == ListenerState::Interactive) {
+ abort();
+ }
+ if (state != ListenerState::None) {
+ callback_thread_.join();
+ }
+}
+
+std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>>
+TrustyConfirmationUI::promptUserConfirmation_(const MsgString& promptText,
+ const MsgVector<uint8_t>& extraData,
+ const MsgString& locale,
+ const MsgVector<teeui::UIOption>& uiOptions) {
+ std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+ /*
+ * This is the main listener thread function. The listener thread life cycle
+ * is equivalent to the life cycle of a single confirmation request. The life
+ * cycle is divided in four phases.
+ * * The starting phase:
+ * * The Trusted App gets loaded and/or the connection to it gets established.
+ * * A connection to the secure input device is established.
+ * * The prompt is initiated. This sends all information required by the
+ * confirmation dialog to the TA. The dialog is not yet displayed.
+ * * An event loop is created.
+ * * The event loop listens for user input events, fetches them from the
+ * secure input device, and delivers them to the TA.
+ * * All evdev devices are grabbed to give confirmationui exclusive access
+ * to user input.
+ *
+ * Note: During the starting phase the hwbinder service thread is blocked and
+ * waiting for possible Errors. If the setup phase concludes successfully, the
+ * hwbinder service thread gets unblocked and returns successfully. Errors
+ * that occur after the first phase are delivered by callback interface.
+ *
+ * * The 2nd phase - non interactive phase
+ * * The event loop thread is started.
+ * * After a grace period:
+ * * A handshake between the secure input device SecureInput and the TA
+ * is performed.
+ * * The input event handler are armed to process user input events.
+ *
+ * * The 3rd phase - interactive phase
+ * * We wait to any external event
+ * * Abort
+ * * Secure user input asserted
+ * * Secure input delivered (for non interactive VTS testing)
+ * * The result is fetched from the TA.
+ *
+ * * The 4th phase - cleanup
+ * The cleanup phase is given by the scope of automatic variables created
+ * in this function. The cleanup commences in reverse order of their creation.
+ * Here is a list of more complex items in the order in which they go out of
+ * scope
+ * * finalizeSecureTouch - signals and joins the secure touch thread.
+ * * eventloop - signals and joins the event loop thread. The event
+ * handlers also own all EventDev instances which ungrab the event devices.
+ * When the eventloop goes out of scope the EventDevs get destroyed
+ * relinquishing the exclusive hold on the event devices.
+ * * finalizeConfirmationPrompt - calls abort on the TA, making sure a
+ * pending operation gets canceled. If the prompt concluded successfully this
+ * is a spurious call but semantically a no op.
+ * * secureInput - shuts down the connection to the secure input device
+ * SecureInput.
+ * * app - disconnects the TA. Since app is a shared pointer this may not
+ * unload the app here. It is possible that more instances of the shared
+ * pointer are held in TrustyConfirmationUI::deliverSecureInputEvent and
+ * TrustyConfirmationUI::abort. But these instances are extremely short lived
+ * and it is safe if they are destroyed by either.
+ * * stateLock - unlocks the listener_state_lock_ if it happens to be held
+ * at the time of return.
+ */
+
+ std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>> result;
+ TeeuiRc& rc = std::get<TeeuiRc>(result);
+ rc = TeeuiRc::SystemError;
+
+ listener_state_ = ListenerState::Starting;
+
+ auto app = std::make_shared<TrustyApp>(kTrustyDeviceName, kConfirmationuiAppName);
+ if (!app) return result; // TeeuiRc::SystemError
+
+ app_ = app;
+
+ auto hsBegin = [&]() -> std::tuple<TeeuiRc, Nonce> {
+ auto [error, result] =
+ app->issueCmd<secure_input::InputHandshake, secure_input::InputHandshakeResponse>();
+ auto& [rc, nCo] = result;
+
+ if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
+ LOG(ERROR) << "Failed to begin secure input handshake (" << int32_t(error) << "/"
+ << uint32_t(rc) << ")";
+ rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
+ }
+ return result;
+ };
+
+ auto hsFinalize = [&](const Signature& sig, const Nonce& nCi) -> TeeuiRc {
+ auto [error, finalizeResponse] =
+ app->issueCmd<FinalizeInputSessionHandshake, FinalizeInputSessionHandshakeResponse>(
+ nCi, sig);
+ auto& [rc] = finalizeResponse;
+ if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
+ LOG(ERROR) << "Failed to finalize secure input handshake (" << int32_t(error) << "/"
+ << uint32_t(rc) << ")";
+ rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
+ }
+ return rc;
+ };
+
+ auto deliverInput = [&](DTupKeyEvent event,
+ const Signature& sig) -> std::tuple<TeeuiRc, InputResponse> {
+ auto [error, result] =
+ app->issueCmd<DeliverInputEvent, DeliverInputEventResponse>(event, sig);
+ auto& [rc, ir] = result;
+ if (error != TrustyAppError::OK) {
+ LOG(ERROR) << "Failed to deliver input command";
+ rc = TeeuiRc::SystemError;
+ }
+ return result;
+ };
+
+ std::atomic<TeeuiRc> eventRC = TeeuiRc::OperationPending;
+ auto inputResult = [&](TeeuiRc rc) {
+ TeeuiRc expected = TeeuiRc::OperationPending;
+ if (eventRC.compare_exchange_strong(expected, rc)) {
+ listener_state_condv_.notify_all();
+ }
+ };
+
+ // create Secure Input device.
+ auto secureInput = createSecureInput(hsBegin, hsFinalize, deliverInput, inputResult);
+ if (!secureInput || !(*secureInput)) {
+ LOG(ERROR) << "Failed to open secure input device";
+ return result; // TeeuiRc::SystemError;
+ }
+
+ Finalize finalizeConfirmationPrompt([app] {
+ LOG(INFO) << "Calling abort for cleanup";
+ app->issueCmd<AbortMsg>();
+ });
+
+ // initiate prompt
+ LOG(INFO) << "Initiating prompt";
+ TrustyAppError error;
+ auto initResponse = std::tie(rc);
+ std::tie(error, initResponse) =
+ app->issueCmd<PromptUserConfirmationMsg, PromptUserConfirmationResponse>(
+ promptText, extraData, locale, uiOptions);
+ if (error == TrustyAppError::MSG_TOO_LONG) {
+ LOG(ERROR) << "PromptUserConfirmationMsg failed: message too long";
+ rc = TeeuiRc::UIErrorMessageTooLong;
+ return result;
+ } else if (error != TrustyAppError::OK) {
+ LOG(ERROR) << "PromptUserConfirmationMsg failed: " << int32_t(error);
+ return result; // TeeuiRc::SystemError;
+ }
+ if (rc != TeeuiRc::OK) {
+ LOG(ERROR) << "PromptUserConfirmationMsg failed: " << uint32_t(rc);
+ return result;
+ }
+
+ LOG(INFO) << "Grabbing event devices";
+ EventLoop eventloop;
+ bool grabbed =
+ grabAllEvDevsAndRegisterCallbacks(&eventloop, [&](short flags, const EventDev& evDev) {
+ if (!(flags & POLLIN)) return;
+ secureInput->handleEvent(evDev);
+ });
+
+ if (!grabbed) {
+ rc = TeeuiRc::SystemError;
+ return result;
+ }
+
+ abort_called_ = false;
+ secureInputDelivered_ = false;
+
+ // ############################## Start 2nd Phase #############################################
+ listener_state_ = ListenerState::SetupDone;
+ stateLock.unlock();
+ listener_state_condv_.notify_all();
+
+ if (!eventloop.start()) {
+ rc = TeeuiRc::SystemError;
+ return result;
+ }
+
+ stateLock.lock();
+
+ LOG(INFO) << "going to sleep for the grace period";
+ auto then = std::chrono::system_clock::now() +
+ std::chrono::milliseconds(kUserPreInputGracePeriodMillis) +
+ std::chrono::microseconds(50);
+ listener_state_condv_.wait_until(stateLock, then, [&]() { return abort_called_; });
+ LOG(INFO) << "waking up";
+
+ if (abort_called_) {
+ LOG(ERROR) << "Abort called";
+ result = {TeeuiRc::Aborted, {}, {}};
+ return result;
+ }
+
+ LOG(INFO) << "Arming event poller";
+ // tell the event poller to act on received input events from now on.
+ secureInput->start();
+
+ // ############################## Start 3rd Phase - interactive phase #########################
+ LOG(INFO) << "Transition to Interactive";
+ listener_state_ = ListenerState::Interactive;
+ stateLock.unlock();
+ listener_state_condv_.notify_all();
+
+ stateLock.lock();
+ listener_state_condv_.wait(stateLock, [&]() {
+ return eventRC != TeeuiRc::OperationPending || abort_called_ || secureInputDelivered_;
+ });
+ LOG(INFO) << "Listener waking up";
+ if (abort_called_) {
+ LOG(ERROR) << "Abort called";
+ result = {TeeuiRc::Aborted, {}, {}};
+ return result;
+ }
+
+ if (!secureInputDelivered_) {
+ if (eventRC != TeeuiRc::OK) {
+ LOG(ERROR) << "Bad input response";
+ result = {eventRC, {}, {}};
+ return result;
+ }
+ }
+
+ stateLock.unlock();
+
+ LOG(INFO) << "Fetching Result";
+ std::tie(error, result) = app->issueCmd<FetchConfirmationResult, ResultMsg>();
+ LOG(INFO) << "Result yields " << int32_t(error) << "/" << uint32_t(rc);
+ if (error != TrustyAppError::OK) {
+ result = {TeeuiRc::SystemError, {}, {}};
+ }
+ return result;
+
+ // ############################## Start 4th Phase - cleanup ##################################
+}
+
+// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+// follow.
+Return<ResponseCode> TrustyConfirmationUI::promptUserConfirmation(
+ const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
+ const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
+ const hidl_vec<UIOption>& uiOptions) {
+ std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
+ if (!stateLock.try_lock()) {
+ return ResponseCode::OperationPending;
+ }
+ switch (listener_state_) {
+ case ListenerState::None:
+ break;
+ case ListenerState::Starting:
+ case ListenerState::SetupDone:
+ case ListenerState::Interactive:
+ return ResponseCode::OperationPending;
+ case ListenerState::Terminating:
+ callback_thread_.join();
+ listener_state_ = ListenerState::None;
+ break;
+ default:
+ return ResponseCode::Unexpected;
+ }
+
+ assert(listener_state_ == ListenerState::None);
+
+ callback_thread_ = std::thread(
+ [this](sp<IConfirmationResultCallback> resultCB, hidl_string promptText,
+ hidl_vec<uint8_t> extraData, hidl_string locale, hidl_vec<UIOption> uiOptions) {
+ auto [trc, msg, token] =
+ promptUserConfirmation_(hidl2MsgString(promptText), hidl2MsgVector(extraData),
+ hidl2MsgString(locale), hidl2MsgVector(uiOptions));
+ bool do_callback = (listener_state_ == ListenerState::Interactive ||
+ listener_state_ == ListenerState::SetupDone) &&
+ resultCB;
+ prompt_result_ = convertRc(trc);
+ listener_state_ = ListenerState::Terminating;
+ if (do_callback) {
+ auto error = resultCB->result(prompt_result_, msg, token);
+ if (!error.isOk()) {
+ LOG(ERROR) << "Result callback failed " << error.description();
+ }
+ } else {
+ listener_state_condv_.notify_all();
+ }
+ },
+ resultCB, promptText, extraData, locale, uiOptions);
+
+ listener_state_condv_.wait(stateLock, [this] {
+ return listener_state_ == ListenerState::SetupDone ||
+ listener_state_ == ListenerState::Interactive ||
+ listener_state_ == ListenerState::Terminating;
+ });
+ if (listener_state_ == ListenerState::Terminating) {
+ callback_thread_.join();
+ listener_state_ = ListenerState::None;
+ return prompt_result_;
+ }
+ return ResponseCode::OK;
+}
+
+Return<ResponseCode>
+TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
+ ResponseCode rc = ResponseCode::Ignored;
+ {
+ /*
+ * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct
+ * implementation responds with a mock confirmation token signed with a test key. The
+ * problem is that the non interactive grace period was not formalized in the HAL spec,
+ * so that the VTS test does not account for the grace period. (It probably should.)
+ * This means we can only pass the VTS test if we block until the grace period is over
+ * (SetupDone -> Interactive) before we deliver the input event.
+ *
+ * The true secure input is delivered by a different mechanism and gets ignored -
+ * not queued - until the grace period is over.
+ *
+ */
+ std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+ listener_state_condv_.wait(stateLock,
+ [this] { return listener_state_ != ListenerState::SetupDone; });
+
+ if (listener_state_ != ListenerState::Interactive) return ResponseCode::Ignored;
+ auto sapp = app_.lock();
+ if (!sapp) return ResponseCode::Ignored;
+ auto [error, response] =
+ sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>(
+ static_cast<teeui::TestModeCommands>(secureInputToken.challenge));
+ if (error != TrustyAppError::OK) return ResponseCode::SystemError;
+ auto& [trc] = response;
+ if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true;
+ rc = convertRc(trc);
+ }
+ if (secureInputDelivered_) listener_state_condv_.notify_all();
+ // VTS test expect an OK response if the event was successfully delivered.
+ // But since the TA returns the callback response now, we have to translate
+ // Canceled into OK. Canceled is only returned if the delivered event canceled
+ // the operation, which means that the event was successfully delivered. Thus
+ // we return OK.
+ if (rc == ResponseCode::Canceled) return ResponseCode::OK;
+ return rc;
+}
+
+Return<void> TrustyConfirmationUI::abort() {
+ {
+ std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+ if (listener_state_ == ListenerState::SetupDone ||
+ listener_state_ == ListenerState::Interactive) {
+ auto sapp = app_.lock();
+ if (sapp) sapp->issueCmd<AbortMsg>();
+ abort_called_ = true;
+ }
+ }
+ listener_state_condv_.notify_all();
+ return Void();
+}
+
+android::sp<IConfirmationUI> createTrustyConfirmationUI() {
+ return new TrustyConfirmationUI();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
diff --git a/guest/hals/confirmationui/TrustyConfirmationUI.h b/guest/hals/confirmationui/TrustyConfirmationUI.h
new file mode 100644
index 0000000..0bd703c
--- /dev/null
+++ b/guest/hals/confirmationui/TrustyConfirmationUI.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020, 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 ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
+#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
+
+#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+#include <android/hardware/keymaster/4.0/types.h>
+#include <hidl/Status.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <teeui/generic_messages.h>
+#include <thread>
+
+#include "TrustyApp.h"
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using ::android::trusty::confirmationui::TrustyApp;
+
+class TrustyConfirmationUI : public IConfirmationUI {
+ public:
+ TrustyConfirmationUI();
+ virtual ~TrustyConfirmationUI();
+ // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+ // follow.
+ Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB,
+ const hidl_string& promptText,
+ const hidl_vec<uint8_t>& extraData,
+ const hidl_string& locale,
+ const hidl_vec<UIOption>& uiOptions) override;
+ Return<ResponseCode> deliverSecureInputEvent(
+ const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override;
+ Return<void> abort() override;
+
+ private:
+ std::weak_ptr<TrustyApp> app_;
+ std::thread callback_thread_;
+
+ enum class ListenerState : uint32_t {
+ None,
+ Starting,
+ SetupDone,
+ Interactive,
+ Terminating,
+ };
+
+ /*
+ * listener_state is protected by listener_state_lock. It makes transitions between phases
+ * of the confirmation operation atomic.
+ * (See TrustyConfirmationUI.cpp#promptUserConfirmation_ for details about operation phases)
+ */
+ ListenerState listener_state_;
+ /*
+ * abort_called_ is also protected by listener_state_lock_ and indicates that the HAL user
+ * called abort.
+ */
+ bool abort_called_;
+ std::mutex listener_state_lock_;
+ std::condition_variable listener_state_condv_;
+ ResponseCode prompt_result_;
+ bool secureInputDelivered_;
+
+ std::tuple<teeui::ResponseCode, teeui::MsgVector<uint8_t>, teeui::MsgVector<uint8_t>>
+ promptUserConfirmation_(const teeui::MsgString& promptText,
+ const teeui::MsgVector<uint8_t>& extraData,
+ const teeui::MsgString& locale,
+ const teeui::MsgVector<teeui::UIOption>& uiOptions);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
diff --git a/guest/hals/confirmationui/android.hardware.confirmationui@1.0-service.cuttlefish.rc b/guest/hals/confirmationui/android.hardware.confirmationui@1.0-service.cuttlefish.rc
new file mode 100644
index 0000000..81dfd49
--- /dev/null
+++ b/guest/hals/confirmationui/android.hardware.confirmationui@1.0-service.cuttlefish.rc
@@ -0,0 +1,5 @@
+service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.cuttlefish
+ interface android.hardware.confirmationui@1.0::IConfirmationUI default
+ class hal
+ user system
+ group drmrpc input system
diff --git a/guest/hals/confirmationui/android.hardware.confirmationui@1.0-service.cuttlefish.xml b/guest/hals/confirmationui/android.hardware.confirmationui@1.0-service.cuttlefish.xml
new file mode 100644
index 0000000..9008b87
--- /dev/null
+++ b/guest/hals/confirmationui/android.hardware.confirmationui@1.0-service.cuttlefish.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.confirmationui</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IConfirmationUI</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/guest/hals/confirmationui/include/TrustyConfirmationuiHal.h b/guest/hals/confirmationui/include/TrustyConfirmationuiHal.h
new file mode 100644
index 0000000..2ab9389
--- /dev/null
+++ b/guest/hals/confirmationui/include/TrustyConfirmationuiHal.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, 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/hardware/confirmationui/1.0/IConfirmationUI.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+android::sp<IConfirmationUI> createTrustyConfirmationUI();
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
diff --git a/guest/hals/confirmationui/include/TrustyIpc.h b/guest/hals/confirmationui/include/TrustyIpc.h
new file mode 100644
index 0000000..eb764bc
--- /dev/null
+++ b/guest/hals/confirmationui/include/TrustyIpc.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 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 <stdint.h>
+
+/*
+ * This interface is shared between Android and Trusty. There is a copy in each
+ * repository. They must be kept in sync.
+ */
+
+#define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui"
+
+/**
+ * enum confirmationui_cmd - command identifiers for ConfirmationUI interface
+ * @CONFIRMATIONUI_RESP_BIT: response bit set as part of response
+ * @CONFIRMATIONUI_REQ_SHIFT: number of bits used by response bit
+ * @CONFIRMATIONUI_CMD_INIT: command to initialize session
+ * @CONFIRMATIONUI_CMD_MSG: command to send ConfirmationUI messages
+ */
+enum confirmationui_cmd : uint32_t {
+ CONFIRMATIONUI_RESP_BIT = 1,
+ CONFIRMATIONUI_REQ_SHIFT = 1,
+
+ CONFIRMATIONUI_CMD_INIT = (1 << CONFIRMATIONUI_REQ_SHIFT),
+ CONFIRMATIONUI_CMD_MSG = (2 << CONFIRMATIONUI_REQ_SHIFT),
+};
+
+/**
+ * struct confirmationui_hdr - header for ConfirmationUI messages
+ * @cmd: command identifier
+ *
+ * Note that no messages return a status code. Any error on the server side
+ * results in the connection being closed. So, operations can be assumed to be
+ * successful if they return a response.
+ */
+struct confirmationui_hdr {
+ uint32_t cmd;
+};
+
+/**
+ * struct confirmationui_init_req - arguments for request to initialize a
+ * session
+ * @shm_len: length of memory region being shared
+ *
+ * A handle to a memory region must be sent along with this message. This memory
+ * is send to ConfirmationUI messages.
+ */
+struct confirmationui_init_req {
+ uint32_t shm_len;
+};
+
+/**
+ * struct confirmationui_msg_args - arguments for sending a message
+ * @msg_len: length of message being sent
+ *
+ * Contents of the message are located in the shared memory region that is
+ * established using %CONFIRMATIONUI_CMD_INIT.
+ *
+ * ConfirmationUI messages can travel both ways.
+ */
+struct confirmationui_msg_args {
+ uint32_t msg_len;
+};
+
+#define CONFIRMATIONUI_MAX_MSG_SIZE 0x2000
diff --git a/guest/hals/confirmationui/service.cpp b/guest/hals/confirmationui/service.cpp
new file mode 100644
index 0000000..dd7e84b
--- /dev/null
+++ b/guest/hals/confirmationui/service.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <TrustyConfirmationuiHal.h>
+
+using android::sp;
+using android::hardware::confirmationui::V1_0::implementation::createTrustyConfirmationUI;
+
+int main() {
+ ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
+ auto service = createTrustyConfirmationUI();
+ auto status = service->registerAsService();
+ if (status != android::OK) {
+ LOG(FATAL) << "Could not register service for ConfirmationUI 1.0 (" << status << ")";
+ return -1;
+ }
+ ::android::hardware::joinRpcThreadpool();
+ return -1;
+}
diff --git a/guest/hals/health/storage/Android.bp b/guest/hals/health/storage/Android.bp
index dc57d0f..1ca807d1 100644
--- a/guest/hals/health/storage/Android.bp
+++ b/guest/hals/health/storage/Android.bp
@@ -38,7 +38,7 @@
],
shared_libs: [
- "android.hardware.health.storage-V1-ndk_platform",
+ "android.hardware.health.storage-V1-ndk",
"libbase",
"libbinder_ndk",
"libutils",
diff --git a/guest/hals/keymint/remote/Android.bp b/guest/hals/keymint/remote/Android.bp
index bb78ff2..26070b4 100644
--- a/guest/hals/keymint/remote/Android.bp
+++ b/guest/hals/keymint/remote/Android.bp
@@ -32,9 +32,10 @@
"-Wextra",
],
shared_libs: [
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.hardware.security.sharedsecret-V1-ndk_platform",
- "android.hardware.security.secureclock-V1-ndk_platform",
+ "android.hardware.security.keymint-V1-ndk",
+ "android.hardware.security.secureclock-V1-ndk",
+ "android.hardware.security.sharedsecret-V1-ndk",
+ "lib_android_keymaster_keymint_utils",
"libbase",
"libbinder_ndk",
"libcppbor_external",
@@ -42,8 +43,8 @@
"libcuttlefish_fs",
"libcuttlefish_security",
"libhardware",
- "libkeymint",
"libkeymaster_messages",
+ "libkeymint",
"liblog",
"libutils",
],
@@ -51,10 +52,15 @@
"remote_keymint_device.cpp",
"remote_keymint_operation.cpp",
"remote_keymaster.cpp",
+ "remote_remotely_provisioned_component.cpp",
"remote_secure_clock.cpp",
+ "remote_shared_secret.cpp",
"service.cpp",
],
defaults: [
"cuttlefish_guest_only",
],
+ required: [
+ "RemoteProvisioner",
+ ],
}
diff --git a/guest/hals/keymint/remote/remote_keymaster.cpp b/guest/hals/keymint/remote/remote_keymaster.cpp
index 4a37cf0..d67b837 100644
--- a/guest/hals/keymint/remote/remote_keymaster.cpp
+++ b/guest/hals/keymint/remote/remote_keymaster.cpp
@@ -22,8 +22,9 @@
namespace keymaster {
-RemoteKeymaster::RemoteKeymaster(cuttlefish::KeymasterChannel* channel)
- : channel_(channel) {}
+RemoteKeymaster::RemoteKeymaster(cuttlefish::KeymasterChannel* channel,
+ uint32_t message_version)
+ : channel_(channel), message_version_(message_version) {}
RemoteKeymaster::~RemoteKeymaster() {}
@@ -66,6 +67,28 @@
return false;
}
+ // Set the vendor patchlevel to value retrieved from system property (which
+ // requires SELinux permission).
+ ConfigureVendorPatchlevelRequest vendor_req(message_version());
+ vendor_req.vendor_patchlevel = GetVendorPatchlevel();
+ ConfigureVendorPatchlevelResponse vendor_rsp =
+ ConfigureVendorPatchlevel(vendor_req);
+ if (vendor_rsp.error != KM_ERROR_OK) {
+ LOG(ERROR) << "Failed to configure keymaster vendor patchlevel: "
+ << vendor_rsp.error;
+ return false;
+ }
+
+ // Set the boot patchlevel to zero for Cuttlefish.
+ ConfigureBootPatchlevelRequest boot_req(message_version());
+ boot_req.boot_patchlevel = 0;
+ ConfigureBootPatchlevelResponse boot_rsp = ConfigureBootPatchlevel(boot_req);
+ if (boot_rsp.error != KM_ERROR_OK) {
+ LOG(ERROR) << "Failed to configure keymaster boot patchlevel: "
+ << boot_rsp.error;
+ return false;
+ }
+
return true;
}
@@ -121,15 +144,25 @@
void RemoteKeymaster::GenerateKey(const GenerateKeyRequest& request,
GenerateKeyResponse* response) {
- GenerateKeyRequest datedRequest(request.message_version);
- datedRequest.key_description = request.key_description;
-
- if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
+ if (message_version_ < MessageVersion(KmVersion::KEYMINT_1) &&
+ !request.key_description.Contains(TAG_CREATION_DATETIME)) {
+ GenerateKeyRequest datedRequest(request.message_version);
datedRequest.key_description.push_back(TAG_CREATION_DATETIME,
java_time(time(NULL)));
+ ForwardCommand(GENERATE_KEY, datedRequest, response);
+ } else {
+ ForwardCommand(GENERATE_KEY, request, response);
}
+}
- ForwardCommand(GENERATE_KEY, datedRequest, response);
+void RemoteKeymaster::GenerateRkpKey(const GenerateRkpKeyRequest& request,
+ GenerateRkpKeyResponse* response) {
+ ForwardCommand(GENERATE_RKP_KEY, request, response);
+}
+
+void RemoteKeymaster::GenerateCsr(const GenerateCsrRequest& request,
+ GenerateCsrResponse* response) {
+ ForwardCommand(GENERATE_CSR, request, response);
}
void RemoteKeymaster::GetKeyCharacteristics(
@@ -230,10 +263,24 @@
return response;
}
-void RemoteKeymaster::GenerateTimestampToken(GenerateTimestampTokenRequest&,
- GenerateTimestampTokenResponse*) {
- // TODO(aosp/1641315): Send a message to the host.
- // ForwardCommand(GENERATE_TIMESTAMP_TOKEN, request, response);
+void RemoteKeymaster::GenerateTimestampToken(
+ GenerateTimestampTokenRequest& request,
+ GenerateTimestampTokenResponse* response) {
+ ForwardCommand(GENERATE_TIMESTAMP_TOKEN, request, response);
+}
+
+ConfigureVendorPatchlevelResponse RemoteKeymaster::ConfigureVendorPatchlevel(
+ const ConfigureVendorPatchlevelRequest& request) {
+ ConfigureVendorPatchlevelResponse response(message_version());
+ ForwardCommand(CONFIGURE_VENDOR_PATCHLEVEL, request, &response);
+ return response;
+}
+
+ConfigureBootPatchlevelResponse RemoteKeymaster::ConfigureBootPatchlevel(
+ const ConfigureBootPatchlevelRequest& request) {
+ ConfigureBootPatchlevelResponse response(message_version());
+ ForwardCommand(CONFIGURE_BOOT_PATCHLEVEL, request, &response);
+ return response;
}
} // namespace keymaster
diff --git a/guest/hals/keymint/remote/remote_keymaster.h b/guest/hals/keymint/remote/remote_keymaster.h
index 6cc6c7b..d7c7abf 100644
--- a/guest/hals/keymint/remote/remote_keymaster.h
+++ b/guest/hals/keymint/remote/remote_keymaster.h
@@ -26,12 +26,14 @@
class RemoteKeymaster {
private:
cuttlefish::KeymasterChannel* channel_;
+ const uint32_t message_version_;
void ForwardCommand(AndroidKeymasterCommand command, const Serializable& req,
KeymasterResponse* rsp);
public:
- RemoteKeymaster(cuttlefish::KeymasterChannel*);
+ RemoteKeymaster(cuttlefish::KeymasterChannel*,
+ uint32_t message_version = kDefaultMessageVersion);
~RemoteKeymaster();
bool Initialize();
void GetVersion(const GetVersionRequest& request,
@@ -53,6 +55,10 @@
void Configure(const ConfigureRequest& request, ConfigureResponse* response);
void GenerateKey(const GenerateKeyRequest& request,
GenerateKeyResponse* response);
+ void GenerateRkpKey(const GenerateRkpKeyRequest& request,
+ GenerateRkpKeyResponse* response);
+ void GenerateCsr(const GenerateCsrRequest& request,
+ GenerateCsrResponse* response);
void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
GetKeyCharacteristicsResponse* response);
void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
@@ -80,12 +86,16 @@
const VerifyAuthorizationRequest& request);
DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);
EarlyBootEndedResponse EarlyBootEnded();
+ ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
+ const ConfigureVendorPatchlevelRequest& request);
+ ConfigureBootPatchlevelResponse ConfigureBootPatchlevel(
+ const ConfigureBootPatchlevelRequest& request);
void GenerateTimestampToken(GenerateTimestampTokenRequest& request,
GenerateTimestampTokenResponse* response);
// CF HAL and remote sides are always compiled together, so will never
// disagree about message versions.
- uint32_t message_version() { return kDefaultMessageVersion; }
+ uint32_t message_version() { return message_version_; }
};
} // namespace keymaster
diff --git a/guest/hals/keymint/remote/remote_keymint_device.cpp b/guest/hals/keymint/remote/remote_keymint_device.cpp
index c9171c8..bef3230 100644
--- a/guest/hals/keymint/remote/remote_keymint_device.cpp
+++ b/guest/hals/keymint/remote/remote_keymint_device.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.security.keymint-impl"
+#define LOG_TAG "android.hardware.security.keymint-impl.remote"
#include <android-base/logging.h>
#include "guest/hals/keymint/remote/remote_keymint_device.h"
@@ -37,17 +37,19 @@
namespace {
vector<KeyCharacteristics> convertKeyCharacteristics(
- SecurityLevel keyMintSecurityLevel, const AuthorizationSet& sw_enforced,
- const AuthorizationSet& hw_enforced) {
+ const vector<KeyParameter>& keyParams, SecurityLevel keyMintSecurityLevel,
+ const AuthorizationSet& sw_enforced, const AuthorizationSet& hw_enforced,
+ bool include_keystore_enforced = true) {
KeyCharacteristics keyMintEnforced{keyMintSecurityLevel, {}};
if (keyMintSecurityLevel != SecurityLevel::SOFTWARE) {
// We're pretending to be TRUSTED_ENVIRONMENT or STRONGBOX.
keyMintEnforced.authorizations = kmParamSet2Aidl(hw_enforced);
- // Put all the software authorizations in the keystore list.
- KeyCharacteristics keystoreEnforced{SecurityLevel::KEYSTORE,
- kmParamSet2Aidl(sw_enforced)};
- return {std::move(keyMintEnforced), std::move(keystoreEnforced)};
+ if (include_keystore_enforced && !sw_enforced.empty()) {
+ return {std::move(keyMintEnforced),
+ {SecurityLevel::KEYSTORE, kmParamSet2Aidl(sw_enforced)}};
+ }
+ return {std::move(keyMintEnforced)};
}
KeyCharacteristics keystoreEnforced{SecurityLevel::KEYSTORE, {}};
@@ -71,15 +73,17 @@
/* Unimplemented */
case KM_TAG_ALLOW_WHILE_ON_BODY:
case KM_TAG_BOOTLOADER_ONLY:
- case KM_TAG_EARLY_BOOT_ONLY:
case KM_TAG_ROLLBACK_RESISTANT:
case KM_TAG_STORAGE_KEY:
- case KM_TAG_TRUSTED_CONFIRMATION_REQUIRED:
- case KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED:
break;
/* Unenforceable */
case KM_TAG_CREATION_DATETIME:
+ for (const auto& p : keyParams) {
+ if (p.tag == Tag::CREATION_DATETIME) {
+ keystoreEnforced.authorizations.push_back(kmParam2Aidl(entry));
+ }
+ }
break;
/* Disallowed in KeyCharacteristics */
@@ -122,6 +126,7 @@
case KM_TAG_BOOT_PATCHLEVEL:
case KM_TAG_CALLER_NONCE:
case KM_TAG_DIGEST:
+ case KM_TAG_EARLY_BOOT_ONLY:
case KM_TAG_EC_CURVE:
case KM_TAG_EXPORTABLE:
case KM_TAG_INCLUDE_UNIQUE_ID:
@@ -140,6 +145,8 @@
case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
case KM_TAG_USER_AUTH_TYPE:
case KM_TAG_USER_SECURE_ID:
+ case KM_TAG_TRUSTED_CONFIRMATION_REQUIRED:
+ case KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED:
case KM_TAG_VENDOR_PATCHLEVEL:
keyMintEnforced.authorizations.push_back(kmParam2Aidl(entry));
break;
@@ -160,10 +167,12 @@
vector<KeyCharacteristics> retval;
retval.reserve(2);
- if (!keyMintEnforced.authorizations.empty())
+ if (!keyMintEnforced.authorizations.empty()) {
retval.push_back(std::move(keyMintEnforced));
- if (!keystoreEnforced.authorizations.empty())
+ }
+ if (include_keystore_enforced && !keystoreEnforced.authorizations.empty()) {
retval.push_back(std::move(keystoreEnforced));
+ }
return retval;
}
@@ -245,7 +254,7 @@
creationResult->keyBlob = kmBlob2vector(response.key_blob);
creationResult->keyCharacteristics = convertKeyCharacteristics(
- securityLevel_, response.unenforced, response.enforced);
+ keyParams, securityLevel_, response.unenforced, response.enforced);
creationResult->certificateChain =
convertCertificateChain(response.certificate_chain);
return ScopedAStatus::ok();
@@ -279,7 +288,7 @@
creationResult->keyBlob = kmBlob2vector(response.key_blob);
creationResult->keyCharacteristics = convertKeyCharacteristics(
- securityLevel_, response.unenforced, response.enforced);
+ keyParams, securityLevel_, response.unenforced, response.enforced);
creationResult->certificateChain =
convertCertificateChain(response.certificate_chain);
@@ -310,7 +319,7 @@
creationResult->keyBlob = kmBlob2vector(response.key_blob);
creationResult->keyCharacteristics = convertKeyCharacteristics(
- securityLevel_, response.unenforced, response.enforced);
+ unwrappingParams, securityLevel_, response.unenforced, response.enforced);
creationResult->certificateChain =
convertCertificateChain(response.certificate_chain);
@@ -358,11 +367,10 @@
return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
}
-ScopedAStatus RemoteKeyMintDevice::begin(KeyPurpose purpose,
- const vector<uint8_t>& keyBlob,
- const vector<KeyParameter>& params,
- const HardwareAuthToken& authToken,
- BeginResult* result) {
+ScopedAStatus RemoteKeyMintDevice::begin(
+ KeyPurpose purpose, const vector<uint8_t>& keyBlob,
+ const vector<KeyParameter>& params,
+ const optional<HardwareAuthToken>& authToken, BeginResult* result) {
BeginOperationRequest request(impl_.message_version());
request.purpose = legacy_enum_conversion(purpose);
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
@@ -413,9 +421,26 @@
return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
}
-ScopedAStatus RemoteKeyMintDevice::performOperation(
- const vector<uint8_t>& /* request */, vector<uint8_t>* /* response */) {
- return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+ScopedAStatus RemoteKeyMintDevice::getKeyCharacteristics(
+ const std::vector<uint8_t>& storageKeyBlob,
+ const std::vector<uint8_t>& appId, const std::vector<uint8_t>& appData,
+ std::vector<KeyCharacteristics>* keyCharacteristics) {
+ GetKeyCharacteristicsRequest request(impl_.message_version());
+ request.SetKeyMaterial(storageKeyBlob.data(), storageKeyBlob.size());
+ addClientAndAppData(appId, appData, &request.additional_params);
+
+ GetKeyCharacteristicsResponse response(impl_.message_version());
+ impl_.GetKeyCharacteristics(request, &response);
+
+ if (response.error != KM_ERROR_OK) {
+ return kmError2ScopedAStatus(response.error);
+ }
+
+ *keyCharacteristics = convertKeyCharacteristics(
+ {} /*keyParams*/, securityLevel_, response.unenforced, response.enforced,
+ false /*include_keystore_enforced*/);
+
+ return ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::security::keymint
diff --git a/guest/hals/keymint/remote/remote_keymint_device.h b/guest/hals/keymint/remote/remote_keymint_device.h
index 7f289b4..c3ebad1 100644
--- a/guest/hals/keymint/remote/remote_keymint_device.h
+++ b/guest/hals/keymint/remote/remote_keymint_device.h
@@ -65,7 +65,7 @@
ScopedAStatus begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
const vector<KeyParameter>& params,
- const HardwareAuthToken& authToken,
+ const optional<HardwareAuthToken>& authToken,
BeginResult* result) override;
ScopedAStatus deviceLocked(
@@ -77,8 +77,10 @@
const std::vector<uint8_t>& storageKeyBlob,
std::vector<uint8_t>* ephemeralKeyBlob) override;
- ScopedAStatus performOperation(const vector<uint8_t>& request,
- vector<uint8_t>* response) override;
+ ScopedAStatus getKeyCharacteristics(
+ const std::vector<uint8_t>& storageKeyBlob,
+ const std::vector<uint8_t>& appId, const std::vector<uint8_t>& appData,
+ std::vector<KeyCharacteristics>* keyCharacteristics) override;
protected:
::keymaster::RemoteKeymaster& impl_;
diff --git a/guest/hals/keymint/remote/remote_keymint_operation.cpp b/guest/hals/keymint/remote/remote_keymint_operation.cpp
index a5fb6aa..b88715a 100644
--- a/guest/hals/keymint/remote/remote_keymint_operation.cpp
+++ b/guest/hals/keymint/remote/remote_keymint_operation.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.security.keymint-impl"
+#define LOG_TAG "android.hardware.security.keymint-impl.remote"
#include <log/log.h>
#include "guest/hals/keymint/remote/remote_keymint_operation.h"
diff --git a/guest/hals/keymint/remote/remote_remotely_provisioned_component.cpp b/guest/hals/keymint/remote/remote_remotely_provisioned_component.cpp
new file mode 100644
index 0000000..7d2eca1
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_remotely_provisioned_component.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 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 <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <keymaster/cppcose/cppcose.h>
+#include <keymaster/keymaster_configuration.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+#include <variant>
+
+#include "KeyMintUtils.h"
+#include "android/binder_auto_utils.h"
+#include "remote_remotely_provisioned_component.h"
+
+namespace aidl::android::hardware::security::keymint {
+namespace {
+using namespace cppcose;
+using namespace keymaster;
+
+using ::aidl::android::hardware::security::keymint::km_utils::kmBlob2vector;
+using ::ndk::ScopedAStatus;
+
+// Error codes from the provisioning stack are negated.
+ndk::ScopedAStatus toKeymasterError(const KeymasterResponse& response) {
+ auto error =
+ static_cast<keymaster_error_t>(-static_cast<int32_t>(response.error));
+ return ::aidl::android::hardware::security::keymint::km_utils::
+ kmError2ScopedAStatus(error);
+}
+
+} // namespace
+
+RemoteRemotelyProvisionedComponent::RemoteRemotelyProvisionedComponent(
+ keymaster::RemoteKeymaster& impl)
+ : impl_(impl) {}
+
+ScopedAStatus RemoteRemotelyProvisionedComponent::getHardwareInfo(
+ RpcHardwareInfo* info) {
+ info->versionNumber = 1;
+ info->rpcAuthorName = "Google";
+ info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteRemotelyProvisionedComponent::generateEcdsaP256KeyPair(
+ bool testMode, MacedPublicKey* macedPublicKey,
+ std::vector<uint8_t>* privateKeyHandle) {
+ GenerateRkpKeyRequest request(impl_.message_version());
+ request.test_mode = testMode;
+ GenerateRkpKeyResponse response(impl_.message_version());
+ impl_.GenerateRkpKey(request, &response);
+ if (response.error != KM_ERROR_OK) {
+ return toKeymasterError(response);
+ }
+
+ macedPublicKey->macedKey = km_utils::kmBlob2vector(response.maced_public_key);
+ *privateKeyHandle = km_utils::kmBlob2vector(response.key_blob);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteRemotelyProvisionedComponent::generateCertificateRequest(
+ bool testMode, const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& endpointEncCertChain,
+ const std::vector<uint8_t>& challenge, DeviceInfo* deviceInfo,
+ ProtectedData* protectedData, std::vector<uint8_t>* keysToSignMac) {
+ GenerateCsrRequest request(impl_.message_version());
+ request.test_mode = testMode;
+ request.num_keys = keysToSign.size();
+ request.keys_to_sign_array = new KeymasterBlob[keysToSign.size()];
+ for (size_t i = 0; i < keysToSign.size(); i++) {
+ request.SetKeyToSign(i, keysToSign[i].macedKey.data(),
+ keysToSign[i].macedKey.size());
+ }
+ request.SetEndpointEncCertChain(endpointEncCertChain.data(),
+ endpointEncCertChain.size());
+ request.SetChallenge(challenge.data(), challenge.size());
+ GenerateCsrResponse response(impl_.message_version());
+ impl_.GenerateCsr(request, &response);
+
+ if (response.error != KM_ERROR_OK) {
+ return toKeymasterError(response);
+ }
+ deviceInfo->deviceInfo = km_utils::kmBlob2vector(response.device_info_blob);
+ protectedData->protectedData =
+ km_utils::kmBlob2vector(response.protected_data_blob);
+ *keysToSignMac = km_utils::kmBlob2vector(response.keys_to_sign_mac);
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/guest/hals/keymint/remote/remote_remotely_provisioned_component.h b/guest/hals/keymint/remote/remote_remotely_provisioned_component.h
new file mode 100644
index 0000000..35e182a
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_remotely_provisioned_component.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021, 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 <cstdint>
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>
+#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
+#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+
+#include "KeyMintUtils.h"
+#include "guest/hals/keymint/remote/remote_keymaster.h"
+
+namespace aidl::android::hardware::security::keymint {
+
+class RemoteRemotelyProvisionedComponent
+ : public BnRemotelyProvisionedComponent {
+ public:
+ explicit RemoteRemotelyProvisionedComponent(keymaster::RemoteKeymaster& impl);
+
+ ndk::ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override;
+
+ ndk::ScopedAStatus generateEcdsaP256KeyPair(
+ bool testMode, MacedPublicKey* macedPublicKey,
+ std::vector<uint8_t>* privateKeyHandle) override;
+
+ ndk::ScopedAStatus generateCertificateRequest(
+ bool testMode, const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& endpointEncCertChain,
+ const std::vector<uint8_t>& challenge, DeviceInfo* deviceInfo,
+ ProtectedData* protectedData,
+ std::vector<uint8_t>* keysToSignMac) override;
+
+ private:
+ keymaster::RemoteKeymaster& impl_;
+};
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/guest/hals/keymint/remote/remote_secure_clock.cpp b/guest/hals/keymint/remote/remote_secure_clock.cpp
index 53bab78..307e0ab 100644
--- a/guest/hals/keymint/remote/remote_secure_clock.cpp
+++ b/guest/hals/keymint/remote/remote_secure_clock.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.security.secureclock-impl"
+#define LOG_TAG "android.hardware.security.secureclock-impl.remote"
#include <log/log.h>
#include "guest/hals/keymint/remote/remote_secure_clock.h"
diff --git a/guest/hals/keymint/remote/remote_shared_secret.cpp b/guest/hals/keymint/remote/remote_shared_secret.cpp
new file mode 100644
index 0000000..6c50900
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_shared_secret.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020, 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 "android.hardware.security.sharedsecret-impl"
+#include <log/log.h>
+
+#include "guest/hals/keymint/remote/remote_shared_secret.h"
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <keymaster/android_keymaster.h>
+#include "KeyMintUtils.h"
+
+namespace aidl::android::hardware::security::sharedsecret {
+
+using namespace ::keymaster;
+using namespace ::aidl::android::hardware::security::keymint::km_utils;
+
+RemoteSharedSecret::RemoteSharedSecret(::keymaster::RemoteKeymaster& keymint)
+ : impl_(keymint) {}
+
+RemoteSharedSecret::~RemoteSharedSecret() {}
+
+ScopedAStatus RemoteSharedSecret::getSharedSecretParameters(
+ SharedSecretParameters* params) {
+ auto response = impl_.GetHmacSharingParameters();
+ params->seed = kmBlob2vector(response.params.seed);
+ params->nonce = {std::begin(response.params.nonce),
+ std::end(response.params.nonce)};
+ return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus RemoteSharedSecret::computeSharedSecret(
+ const vector<SharedSecretParameters>& params,
+ vector<uint8_t>* sharingCheck) {
+ ComputeSharedHmacRequest request(impl_.message_version());
+ request.params_array.params_array =
+ new keymaster::HmacSharingParameters[params.size()];
+ request.params_array.num_params = params.size();
+ for (size_t i = 0; i < params.size(); ++i) {
+ request.params_array.params_array[i].seed = {params[i].seed.data(),
+ params[i].seed.size()};
+ if (sizeof(request.params_array.params_array[i].nonce) !=
+ params[i].nonce.size()) {
+ return kmError2ScopedAStatus(KM_ERROR_INVALID_ARGUMENT);
+ }
+ memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),
+ params[i].nonce.size());
+ }
+ auto response = impl_.ComputeSharedHmac(request);
+ if (response.error == KM_ERROR_OK)
+ *sharingCheck = kmBlob2vector(response.sharing_check);
+ return kmError2ScopedAStatus(response.error);
+}
+
+} // namespace aidl::android::hardware::security::sharedsecret
diff --git a/guest/hals/keymint/remote/remote_shared_secret.h b/guest/hals/keymint/remote/remote_shared_secret.h
new file mode 100644
index 0000000..9c1aae2
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_shared_secret.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020, 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 <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
+#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
+#include "guest/hals/keymint/remote/remote_keymaster.h"
+
+namespace keymaster {
+class AndroidKeymaster;
+}
+namespace aidl::android::hardware::security::sharedsecret {
+using ::ndk::ScopedAStatus;
+using std::shared_ptr;
+using std::vector;
+
+class RemoteSharedSecret : public BnSharedSecret {
+ public:
+ explicit RemoteSharedSecret(::keymaster::RemoteKeymaster& keymint);
+ virtual ~RemoteSharedSecret();
+ ScopedAStatus getSharedSecretParameters(
+ SharedSecretParameters* params) override;
+ ScopedAStatus computeSharedSecret(
+ const vector<SharedSecretParameters>& params,
+ vector<uint8_t>* sharingCheck) override;
+
+ private:
+ ::keymaster::RemoteKeymaster& impl_;
+};
+} // namespace aidl::android::hardware::security::sharedsecret
diff --git a/guest/hals/keymint/remote/service.cpp b/guest/hals/keymint/remote/service.cpp
index 0591edf..f27b0dc 100644
--- a/guest/hals/keymint/remote/service.cpp
+++ b/guest/hals/keymint/remote/service.cpp
@@ -14,32 +14,43 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.security.keymint-service"
+#define LOG_TAG "android.hardware.security.keymint-service.remote"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/km_version.h>
#include <keymaster/soft_keymaster_logger.h>
-#include "guest/hals/keymint/remote/remote_keymint_device.h"
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
#include <guest/hals/keymint/remote/remote_keymaster.h>
#include <guest/hals/keymint/remote/remote_keymint_device.h>
+#include <guest/hals/keymint/remote/remote_remotely_provisioned_component.h>
#include <guest/hals/keymint/remote/remote_secure_clock.h>
+#include <guest/hals/keymint/remote/remote_shared_secret.h>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/security/keymaster_channel.h"
-static const char device[] = "/dev/hvc3";
+namespace {
+
+const char device[] = "/dev/hvc3";
using aidl::android::hardware::security::keymint::RemoteKeyMintDevice;
+using aidl::android::hardware::security::keymint::
+ RemoteRemotelyProvisionedComponent;
using aidl::android::hardware::security::keymint::SecurityLevel;
using aidl::android::hardware::security::secureclock::RemoteSecureClock;
+using aidl::android::hardware::security::sharedsecret::RemoteSharedSecret;
+using keymaster::GenerateTimestampTokenRequest;
+using keymaster::GenerateTimestampTokenResponse;
template <typename T, class... Args>
-static std::shared_ptr<T> addService(Args&&... args) {
+std::shared_ptr<T> addService(Args&&... args) {
std::shared_ptr<T> ser =
ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
- auto instanceName = std::string(T::descriptor) + "/remote";
+ auto instanceName = std::string(T::descriptor) + "/default";
LOG(INFO) << "adding keymint service instance: " << instanceName;
binder_status_t status =
AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
@@ -47,7 +58,23 @@
return ser;
}
-int main() {
+SecurityLevel getSecurityLevel(::keymaster::RemoteKeymaster& remote_keymaster) {
+ GenerateTimestampTokenRequest request(remote_keymaster.message_version());
+ GenerateTimestampTokenResponse response(remote_keymaster.message_version());
+ remote_keymaster.GenerateTimestampToken(request, &response);
+
+ if (response.error != STATUS_OK) {
+ LOG(FATAL) << "Error getting timestamp token from remote keymaster: "
+ << response.error;
+ }
+
+ return static_cast<SecurityLevel>(response.token.security_level);
+}
+
+} // namespace
+
+int main(int, char** argv) {
+ android::base::InitLogging(argv, android::base::KernelLogger);
// Zero threads seems like a useless pool, but below we'll join this thread to
// it, increasing the pool size to 1.
ABinderProcess_setThreadPoolMaxThreadCount(0);
@@ -64,11 +91,19 @@
cuttlefish::KeymasterChannel keymasterChannel(fd, fd);
- keymaster::RemoteKeymaster remote_keymaster(&keymasterChannel);
+ keymaster::RemoteKeymaster remote_keymaster(
+ &keymasterChannel, keymaster::MessageVersion(
+ keymaster::KmVersion::KEYMINT_1, 0 /* km_date */));
+
+ if (!remote_keymaster.Initialize()) {
+ LOG(FATAL) << "Could not initialize keymaster";
+ }
addService<RemoteKeyMintDevice>(remote_keymaster,
- SecurityLevel::TRUSTED_ENVIRONMENT);
+ getSecurityLevel(remote_keymaster));
addService<RemoteSecureClock>(remote_keymaster);
+ addService<RemoteSharedSecret>(remote_keymaster);
+ addService<RemoteRemotelyProvisionedComponent>(remote_keymaster);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
diff --git a/guest/hals/ril/reference-libril/ril_commands.h b/guest/hals/ril/reference-libril/ril_commands.h
index 81629b0..a100b48 100644
--- a/guest/hals/ril/reference-libril/ril_commands.h
+++ b/guest/hals/ril/reference-libril/ril_commands.h
@@ -40,7 +40,7 @@
{RIL_REQUEST_RADIO_POWER, radio_1_6::setRadioPowerResponse},
{RIL_REQUEST_DTMF, radio_1_6::sendDtmfResponse},
{RIL_REQUEST_SEND_SMS, radio_1_6::sendSmsResponse},
- {RIL_REQUEST_SEND_SMS_EXPECT_MORE, radio_1_6::sendSMSExpectMoreResponse},
+ {RIL_REQUEST_SEND_SMS_EXPECT_MORE, radio_1_6::sendSmsExpectMoreResponse},
{RIL_REQUEST_SETUP_DATA_CALL, radio_1_6::setupDataCallResponse},
{RIL_REQUEST_SIM_IO, radio_1_6::iccIOForAppResponse},
{RIL_REQUEST_SEND_USSD, radio_1_6::sendUssdResponse},
diff --git a/guest/hals/ril/reference-libril/ril_config.cpp b/guest/hals/ril/reference-libril/ril_config.cpp
index 9da85b6..5a72db9 100644
--- a/guest/hals/ril/reference-libril/ril_config.cpp
+++ b/guest/hals/ril/reference-libril/ril_config.cpp
@@ -23,8 +23,6 @@
#include <android/hardware/radio/config/1.3/IRadioConfig.h>
#include <android/hardware/radio/config/1.2/IRadioConfigIndication.h>
#include <android/hardware/radio/1.1/types.h>
-#include <android/hardware/radio/1.3/types.h>
-
#include <ril.h>
#include <guest/hals/ril/reference-libril/ril_service.h>
@@ -499,9 +497,15 @@
::android::hardware::radio::V1_6::RadioResponseInfo responseInfo = {};
populateResponseInfo_1_6(responseInfo, serial, responseType, e);
- V1_3::HalDeviceCapabilities halCap = {};
+ bool modemReducedFeatureSet1 = false;
+ if (response == NULL || responseLen != sizeof(bool)) {
+ RLOGE("getHalDeviceCapabilitiesResponse Invalid response.");
+ } else {
+ modemReducedFeatureSet1 = (*((bool *) response));
+ }
+
Return<void> retStatus = radioConfigService->mRadioConfigResponseV1_3->getHalDeviceCapabilitiesResponse(
- responseInfo, halCap);
+ responseInfo, modemReducedFeatureSet1);
radioConfigService->checkReturnStatus_config(retStatus);
} else {
RLOGE("getHalDeviceCapabilitiesResponse: radioConfigService->getHalDeviceCapabilities == NULL");
diff --git a/guest/hals/ril/reference-libril/ril_service.cpp b/guest/hals/ril/reference-libril/ril_service.cpp
index 848d4dd..0d18ce8 100644
--- a/guest/hals/ril/reference-libril/ril_service.cpp
+++ b/guest/hals/ril/reference-libril/ril_service.cpp
@@ -624,7 +624,7 @@
const ::android::hardware::radio::V1_6::OptionalTrafficDescriptor& trafficDescriptor,
bool matchAllRuleAllowed);
Return<void> sendSms_1_6(int32_t serial, const GsmSmsMessage& message);
- Return<void> sendSMSExpectMore_1_6(int32_t serial, const GsmSmsMessage& message);
+ Return<void> sendSmsExpectMore_1_6(int32_t serial, const GsmSmsMessage& message);
Return<void> sendCdmaSms_1_6(int32_t serial, const CdmaSmsMessage& sms);
Return<void> sendCdmaSmsExpectMore_1_6(int32_t serial, const CdmaSmsMessage& sms);
Return<void> setRadioPower_1_6(int32_t serial, bool powerOn, bool forEmergencyCall,
@@ -641,7 +641,7 @@
Return<void> getSystemSelectionChannels(int32_t serial);
Return<void> getVoiceRegistrationState_1_6(int32_t serial);
Return<void> getDataRegistrationState_1_6(int32_t serial);
- Return<void> getAllowedNetworkTypesBitmap(uint32_t serial);
+ Return<void> getAllowedNetworkTypesBitmap(int32_t serial);
Return<void> getSlicingConfig(int32_t serial);
Return<void> setCarrierInfoForImsiEncryption_1_6(
int32_t serial,
@@ -1332,16 +1332,16 @@
Return<void> RadioImpl_1_6::sendSMSExpectMore(int32_t serial, const GsmSmsMessage& message) {
#if VDBG
- RLOGD("sendSMSExpectMore: serial %d", serial);
+ RLOGD("sendSmsExpectMore: serial %d", serial);
#endif
dispatchStrings(serial, mSlotId, RIL_REQUEST_SEND_SMS_EXPECT_MORE, false,
2, message.smscPdu.c_str(), message.pdu.c_str());
return Void();
}
-Return<void> RadioImpl_1_6::sendSMSExpectMore_1_6(int32_t serial, const GsmSmsMessage& message) {
+Return<void> RadioImpl_1_6::sendSmsExpectMore_1_6(int32_t serial, const GsmSmsMessage& message) {
#if VDBG
- RLOGD("sendSMSExpectMore: serial %d", serial);
+ RLOGD("sendSmsExpectMore: serial %d", serial);
#endif
dispatchStrings(serial, mSlotId, RIL_REQUEST_SEND_SMS_EXPECT_MORE, false,
2, message.smscPdu.c_str(), message.pdu.c_str());
@@ -3932,7 +3932,7 @@
return Void();
}
-Return<void> RadioImpl_1_6::getAllowedNetworkTypesBitmap(uint32_t serial) {
+Return<void> RadioImpl_1_6::getAllowedNetworkTypesBitmap(int32_t serial) {
#if VDBG
RLOGD("getAllowedNetworkTypesBitmap: serial %d", serial);
#endif
@@ -6347,15 +6347,15 @@
regResponse.accessTechnologySpecificInfo.cdmaInfo(cdmaInfo);
} else if (rat == RADIO_TECH_NR) {
// rat is NR only for NR SA
- V1_6::RegStateResult::AccessTechnologySpecificInfo::
- NgranRegistrationInfo ngranInfo;
- ngranInfo.nrVopsInfo.vopsSupported =
+ V1_6::NrVopsInfo nrVopsInfo;
+ nrVopsInfo.vopsSupported =
::android::hardware::radio::V1_6::VopsIndicator::VOPS_NOT_SUPPORTED;
- ngranInfo.nrVopsInfo.emcSupported =
+ nrVopsInfo.emcSupported =
::android::hardware::radio::V1_6::EmcIndicator::EMC_NOT_SUPPORTED;
- ngranInfo.nrVopsInfo.emfSupported =
+ nrVopsInfo.emfSupported =
::android::hardware::radio::V1_6::EmfIndicator::EMF_NOT_SUPPORTED;
- regResponse.accessTechnologySpecificInfo.ngranInfo(ngranInfo);
+ regResponse.accessTechnologySpecificInfo.ngranNrVopsInfo(nrVopsInfo);
+
} else {
V1_5::RegStateResult::AccessTechnologySpecificInfo::
EutranRegistrationInfo eutranInfo;
@@ -6524,15 +6524,15 @@
numStrings, resp);
if (rat == RADIO_TECH_NR) {
// rat is NR only for NR SA
- V1_6::RegStateResult::AccessTechnologySpecificInfo::
- NgranRegistrationInfo ngranInfo;
- ngranInfo.nrVopsInfo.vopsSupported =
+ V1_6::NrVopsInfo nrVopsInfo;
+ nrVopsInfo.vopsSupported =
::android::hardware::radio::V1_6::VopsIndicator::VOPS_NOT_SUPPORTED;
- ngranInfo.nrVopsInfo.emcSupported =
+ nrVopsInfo.emcSupported =
::android::hardware::radio::V1_6::EmcIndicator::EMC_NOT_SUPPORTED;
- ngranInfo.nrVopsInfo.emfSupported =
+ nrVopsInfo.emfSupported =
::android::hardware::radio::V1_6::EmfIndicator::EMF_NOT_SUPPORTED;
- regResponse.accessTechnologySpecificInfo.ngranInfo(ngranInfo);
+ regResponse.accessTechnologySpecificInfo.ngranNrVopsInfo(nrVopsInfo);
+
} else {
V1_5::RegStateResult::AccessTechnologySpecificInfo::
EutranRegistrationInfo eutranInfo;
@@ -6809,11 +6809,11 @@
return 0;
}
-int radio_1_6::sendSMSExpectMoreResponse(int slotId,
+int radio_1_6::sendSmsExpectMoreResponse(int slotId,
int responseType, int serial, RIL_Errno e, void *response,
size_t responseLen) {
#if VDBG
- RLOGD("sendSMSExpectMoreResponse: serial %d", serial);
+ RLOGD("sendSmsExpectMoreResponse: serial %d", serial);
#endif
if (radioService[slotId]->mRadioResponseV1_6 != NULL) {
@@ -6822,7 +6822,7 @@
responseLen);
Return<void> retStatus = radioService[slotId]->mRadioResponseV1_6
- ->sendSMSExpectMoreResponse_1_6(responseInfo_1_6, result);
+ ->sendSmsExpectMoreResponse_1_6(responseInfo_1_6, result);
radioService[slotId]->checkReturnStatus(retStatus);
} else if (radioService[slotId]->mRadioResponse != NULL) {
RadioResponseInfo responseInfo = {};
diff --git a/guest/hals/ril/reference-libril/ril_service.h b/guest/hals/ril/reference-libril/ril_service.h
index 30c7b69..6821f2c 100644
--- a/guest/hals/ril/reference-libril/ril_service.h
+++ b/guest/hals/ril/reference-libril/ril_service.h
@@ -117,7 +117,7 @@
int responseType, int serial, RIL_Errno e, void *response,
size_t responselen);
-int sendSMSExpectMoreResponse(int slotId,
+int sendSmsExpectMoreResponse(int slotId,
int responseType, int serial, RIL_Errno e, void *response,
size_t responselen);
diff --git a/guest/hals/ril/reference-ril/reference-ril.c b/guest/hals/ril/reference-ril/reference-ril.c
index c1d049d..51cc3ab 100644
--- a/guest/hals/ril/reference-ril/reference-ril.c
+++ b/guest/hals/ril/reference-ril/reference-ril.c
@@ -63,8 +63,8 @@
#else
#define PPP_TTY_PATH_ETH0 "eth0"
#endif
-// This is used if Wifi is supported to separate radio and wifi interface
-#define PPP_TTY_PATH_RADIO0 "radio0"
+// This is used for emulator
+#define EMULATOR_RADIO_INTERFACE "eth0"
// for sim
#define AUTH_CONTEXT_EAP_SIM 128
@@ -542,9 +542,9 @@
}
#ifdef CUTTLEFISH_ENABLE
-static void set_Ip_Addr(const char *addr) {
+static void set_Ip_Addr(const char *addr, const char* radioInterfaceName) {
RLOGD("%s %d setting ip addr %s on interface %s", __func__, __LINE__, addr,
- PPP_TTY_PATH_ETH0);
+ radioInterfaceName);
struct ifreq request;
int status = 0;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
@@ -554,7 +554,7 @@
}
memset(&request, 0, sizeof(request));
- strncpy(request.ifr_name, PPP_TTY_PATH_ETH0, sizeof(request.ifr_name));
+ strncpy(request.ifr_name, radioInterfaceName, sizeof(request.ifr_name));
request.ifr_name[sizeof(request.ifr_name) - 1] = '\0';
char *myaddr = strdup(addr);
@@ -780,15 +780,12 @@
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
}
-static bool hasWifiCapability()
+static const char* getRadioInterfaceName()
{
- char propValue[PROP_VALUE_MAX];
- return property_get("ro.boot.qemu.wifi", propValue, "") > 0 && strcmp("1", propValue) == 0;
-}
-
-static const char* getRadioInterfaceName(bool hasWifi)
-{
- return hasWifi ? PPP_TTY_PATH_RADIO0 : PPP_TTY_PATH_ETH0;
+ if (isInEmulator()) {
+ return EMULATOR_RADIO_INTERFACE;
+ }
+ return PPP_TTY_PATH_ETH0;
}
static void requestOrSendDataCallList(int cid, RIL_Token *t)
@@ -799,8 +796,7 @@
int n = 0;
char *out = NULL;
char propValue[PROP_VALUE_MAX] = {0};
- bool hasWifi = hasWifiCapability();
- const char* radioInterfaceName = getRadioInterfaceName(hasWifi);
+ const char* radioInterfaceName = getRadioInterfaceName();
err = at_send_command_multiline ("AT+CGACT?", "+CGACT:", &p_response);
if (err != 0 || p_response->success == 0) {
@@ -912,7 +908,7 @@
responses[i].addresses = alloca(addresses_size);
strlcpy(responses[i].addresses, out, addresses_size);
#ifdef CUTTLEFISH_ENABLE
- set_Ip_Addr(responses[i].addresses);
+ set_Ip_Addr(responses[i].addresses, radioInterfaceName);
#endif
if (isInEmulator()) {
@@ -949,12 +945,7 @@
}
responses[i].dnses = dnslist;
- /* There is only one gateway in the emulator. If WiFi is
- * configured the interface visible to RIL will be behind a NAT
- * where the gateway is different. */
- if (hasWifi) {
- responses[i].gateways = "192.168.200.1";
- } else if (property_get("vendor.net.eth0.gw", propValue, "") > 0) {
+ if (property_get("vendor.net.eth0.gw", propValue, "") > 0) {
responses[i].gateways = propValue;
} else {
responses[i].gateways = "";
@@ -2778,8 +2769,7 @@
if (qmistatus < 0) goto error;
} else {
- bool hasWifi = hasWifiCapability();
- const char* radioInterfaceName = getRadioInterfaceName(hasWifi);
+ const char* radioInterfaceName = getRadioInterfaceName();
if (setInterfaceState(radioInterfaceName, kInterfaceUp) != RIL_E_SUCCESS) {
goto error;
}
@@ -2838,8 +2828,7 @@
return;
}
- bool hasWifi = hasWifiCapability();
- const char* radioInterfaceName = getRadioInterfaceName(hasWifi);
+ const char* radioInterfaceName = getRadioInterfaceName();
rilErrno = setInterfaceState(radioInterfaceName, kInterfaceDown);
RIL_onRequestComplete(t, rilErrno, NULL, 0);
putPDP(cid);
@@ -3240,7 +3229,7 @@
s_lac, // lac
s_cid, // cid
0, //arfcn unknown
- 0xFF, // bsic unknown
+ 0x1, // Base Station Identity Code set to arbitrarily 1
},
{ // gsm.signalStrengthGsm
10, // signalStrength
@@ -4229,6 +4218,16 @@
pSimSlotStatus->eid = "";
}
+void sendUnsolNetworkScanResult() {
+ RIL_NetworkScanResult scanr;
+ memset(&scanr, 0, sizeof(scanr));
+ scanr.status = COMPLETE;
+ scanr.error = RIL_E_SUCCESS;
+ scanr.network_infos = NULL;
+ scanr.network_infos_length = 0;
+ RIL_onUnsolicitedResponse(RIL_UNSOL_NETWORK_SCAN_RESULT, &scanr, sizeof(scanr));
+}
+
void onIccSlotStatus(RIL_Token t) {
RIL_SimSlotStatus_V1_2 *pSimSlotStatusList =
(RIL_SimSlotStatus_V1_2 *)calloc(SIM_COUNT, sizeof(RIL_SimSlotStatus_V1_2));
@@ -4847,6 +4846,8 @@
// New requests after P.
case RIL_REQUEST_START_NETWORK_SCAN:
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+ // send unsol network scan results after a short while
+ RIL_requestTimedCallback (sendUnsolNetworkScanResult, NULL, &TIMEVAL_SIMPOLL);
break;
case RIL_REQUEST_GET_MODEM_STACK_STATUS:
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
@@ -4913,9 +4914,15 @@
phoneCapability, sizeof(RIL_PhoneCapability));
break;
}
- case RIL_REQUEST_CONFIG_SET_MODEM_CONFIG:
- RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+ case RIL_REQUEST_CONFIG_SET_MODEM_CONFIG: {
+ RIL_ModemConfig *mdConfig = (RIL_ModemConfig*)(data);
+ if (mdConfig == NULL || mdConfig->numOfLiveModems != 1) {
+ RIL_onRequestComplete(t, RIL_E_INVALID_ARGUMENTS, NULL, 0);
+ } else {
+ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+ }
break;
+ }
case RIL_REQUEST_CONFIG_GET_MODEM_CONFIG: {
RIL_ModemConfig mdConfig;
mdConfig.numOfLiveModems = 1;
@@ -4923,10 +4930,15 @@
RIL_onRequestComplete(t, RIL_E_SUCCESS, &mdConfig, sizeof(RIL_ModemConfig));
break;
}
- case RIL_REQUEST_CONFIG_SET_PREFER_DATA_MODEM:
- RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+ case RIL_REQUEST_CONFIG_SET_PREFER_DATA_MODEM: {
+ int *modemId = (int*)(data);
+ if (modemId == NULL || *modemId != 0) {
+ RIL_onRequestComplete(t, RIL_E_INVALID_ARGUMENTS, NULL, 0);
+ } else {
+ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+ }
break;
-
+ }
case RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA:
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
break;
diff --git a/guest/libs/Android.mk b/guest/libs/Android.mk
new file mode 100644
index 0000000..428f4b5
--- /dev/null
+++ b/guest/libs/Android.mk
@@ -0,0 +1,18 @@
+# Copyright (C) 2021 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)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/libs/wpa_supplicant_8_lib/Android.mk b/guest/libs/wpa_supplicant_8_lib/Android.mk
new file mode 100644
index 0000000..31179f5
--- /dev/null
+++ b/guest/libs/wpa_supplicant_8_lib/Android.mk
@@ -0,0 +1,82 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(WPA_SUPPLICANT_VERSION),VER_0_8_X)
+
+ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),)
+ CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y
+endif
+
+# Use a custom libnl on releases before N
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?))
+EXTERNAL_VSOC_LIBNL_INCLUDE := external/gce/libnl/include
+else
+EXTERNAL_VSOC_LIBNL_INCLUDE :=
+endif
+
+
+WPA_SUPPL_DIR = external/wpa_supplicant_8
+WPA_SRC_FILE :=
+
+include $(WPA_SUPPL_DIR)/wpa_supplicant/android.config
+
+WPA_SUPPL_DIR_INCLUDE = $(WPA_SUPPL_DIR)/src \
+ $(WPA_SUPPL_DIR)/src/common \
+ $(WPA_SUPPL_DIR)/src/drivers \
+ $(WPA_SUPPL_DIR)/src/l2_packet \
+ $(WPA_SUPPL_DIR)/src/utils \
+ $(WPA_SUPPL_DIR)/src/wps \
+ $(WPA_SUPPL_DIR)/wpa_supplicant \
+ $(EXTERNAL_VSOC_LIBNL_INCLUDE)
+
+WPA_SUPPL_DIR_INCLUDE += external/libnl/include
+
+ifdef CONFIG_DRIVER_NL80211
+WPA_SRC_FILE += driver_cmd_nl80211.c
+endif
+
+ifeq ($(TARGET_ARCH),arm)
+# To force sizeof(enum) = 4
+L_CFLAGS += -mabi=aapcs-linux
+endif
+
+ifdef CONFIG_ANDROID_LOG
+L_CFLAGS += -DCONFIG_ANDROID_LOG
+endif
+
+########################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := lib_driver_cmd_simulated_cf
+LOCAL_VENDOR_MODULE := true
+LOCAL_SHARED_LIBRARIES := libc libcutils
+
+LOCAL_CFLAGS := $(L_CFLAGS) \
+ $(VSOC_VERSION_CFLAGS)
+
+LOCAL_SRC_FILES := $(WPA_SRC_FILE)
+
+LOCAL_C_INCLUDES := \
+ device/google/cuttlefish_common \
+ $(WPA_SUPPL_DIR_INCLUDE)\
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+include $(BUILD_STATIC_LIBRARY)
+
+########################
+
+endif
diff --git a/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.c b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.c
new file mode 100644
index 0000000..7181498
--- /dev/null
+++ b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.c
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+/*
+ * Driver interaction with extended Linux CFG8021
+ */
+
+#include "driver_cmd_nl80211.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <netinet/in.h>
+#include "driver_nl80211.h"
+
+#include "android_drv.h"
+#include "common.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+
+int wpa_driver_nl80211_driver_cmd(void* priv, char* cmd, char* buf,
+ size_t buf_len) {
+ struct i802_bss* bss = priv;
+ struct wpa_driver_nl80211_data* drv = bss->drv;
+ struct ifreq ifr;
+ android_wifi_priv_cmd priv_cmd;
+ int ret = 0;
+
+ D("%s: called", __FUNCTION__);
+ if (os_strcasecmp(cmd, "STOP") == 0) {
+ linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
+ } else if (os_strcasecmp(cmd, "START") == 0) {
+ linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
+ } else if (os_strcasecmp(cmd, "MACADDR") == 0) {
+ u8 macaddr[ETH_ALEN] = {};
+
+ ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, macaddr);
+ if (!ret)
+ ret =
+ os_snprintf(buf, buf_len, "Macaddr = " MACSTR "\n", MAC2STR(macaddr));
+ } else if (os_strcasecmp(cmd, "RELOAD") == 0) {
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+ } else { // Use private command
+ return 0;
+ }
+ return ret;
+}
+
+int wpa_driver_set_p2p_noa(void* priv, u8 count, int start, int duration) {
+ D("%s: called", __FUNCTION__);
+ return 0;
+}
+
+int wpa_driver_get_p2p_noa(void* priv, u8* buf, size_t len) {
+ D("%s: called", __FUNCTION__);
+ return 0;
+}
+
+int wpa_driver_set_p2p_ps(void* priv, int legacy_ps, int opp_ps, int ctwindow) {
+ D("%s: called", __FUNCTION__);
+ return -1;
+}
+
+int wpa_driver_set_ap_wps_p2p_ie(void* priv, const struct wpabuf* beacon,
+ const struct wpabuf* proberesp,
+ const struct wpabuf* assocresp) {
+ D("%s: called", __FUNCTION__);
+ return 0;
+}
diff --git a/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.h b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.h
new file mode 100644
index 0000000..0970a63
--- /dev/null
+++ b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.h
@@ -0,0 +1,40 @@
+#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 <linux/if_ether.h>
+#include <memory.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common.h"
+#include "linux_ioctl.h"
+#include "wpa_supplicant_i.h"
+
+#define VSOC_WPA_SUPPLICANT_DEBUG 0
+
+#if VSOC_WPA_SUPPLICANT_DEBUG
+#define D(...) ALOGD(__VA_ARGS__)
+#else
+#define D(...) ((void)0)
+#endif
+
+typedef struct android_wifi_priv_cmd {
+ char* buf;
+ int used_len;
+ int total_len;
+} android_wifi_priv_cmd;
diff --git a/guest/monitoring/cuttlefish_service/Android.bp b/guest/monitoring/cuttlefish_service/Android.bp
index 133d233..d33d0e7 100644
--- a/guest/monitoring/cuttlefish_service/Android.bp
+++ b/guest/monitoring/cuttlefish_service/Android.bp
@@ -20,6 +20,9 @@
name: "CuttlefishService",
vendor: true,
srcs: ["java/**/*.java"],
+ resource_dirs: [
+ "res",
+ ],
static_libs: ["guava"],
sdk_version: "28",
privileged: true,
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/BluetoothChecker.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/BluetoothChecker.java
index 23d4fec..000a96d 100644
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/BluetoothChecker.java
+++ b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/BluetoothChecker.java
@@ -16,6 +16,8 @@
package com.android.google.gce.gceservice;
import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.content.res.Resources;
import android.util.Log;
/*
@@ -28,8 +30,13 @@
private final GceFuture<Boolean> mEnabled = new GceFuture<Boolean>("Bluetooth");
- public BluetoothChecker() {
+ public BluetoothChecker(Context context) {
super(LOG_TAG);
+ Resources res = context.getResources();
+ if (!res.getBoolean(R.bool.config_bt_required)) {
+ Log.i(LOG_TAG, "Bluetooth not required, assuming enabled.");
+ mEnabled.set(true);
+ }
}
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceService.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceService.java
index 80d497a..ef0aa03 100644
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceService.java
+++ b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceService.java
@@ -51,8 +51,8 @@
private final JobExecutor mExecutor = new JobExecutor();
private final EventReporter mEventReporter = new EventReporter();
private final GceBroadcastReceiver mBroadcastReceiver = new GceBroadcastReceiver();
- private final BluetoothChecker mBluetoothChecker = new BluetoothChecker();
+ private BluetoothChecker mBluetoothChecker = null;
private ConnectivityChecker mConnChecker;
private GceWifiManager mWifiManager = null;
private String mMostRecentAction = null;
@@ -76,6 +76,7 @@
mWindowManager = getSystemService(WindowManager.class);
mConnChecker = new ConnectivityChecker(this, mEventReporter);
mWifiManager = new GceWifiManager(this, mEventReporter, mExecutor);
+ mBluetoothChecker = new BluetoothChecker(this);
mPreviousRotation = getRotation();
mPreviousScreenBounds = getScreenBounds();
diff --git a/guest/monitoring/cuttlefish_service/res/values/config.xml b/guest/monitoring/cuttlefish_service/res/values/config.xml
new file mode 100644
index 0000000..692a686
--- /dev/null
+++ b/guest/monitoring/cuttlefish_service/res/values/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources>
+ <!-- Is BT required for boot complete -->
+ <bool name="config_bt_required">true</bool>
+</resources>
diff --git a/guest/monitoring/tombstone_transmit/tombstone_transmit.cpp b/guest/monitoring/tombstone_transmit/tombstone_transmit.cpp
index 3d08780..b283fef 100644
--- a/guest/monitoring/tombstone_transmit/tombstone_transmit.cpp
+++ b/guest/monitoring/tombstone_transmit/tombstone_transmit.cpp
@@ -123,8 +123,9 @@
}
if (!log_fd->IsOpen()) {
+ auto error = log_fd->StrError();
ALOGE("Unable to connect to vsock:%u:%u: %s", FLAGS_cid, FLAGS_port,
- log_fd->StrError());
+ error.c_str());
} else if (!ifs.is_open()) {
ALOGE("%s closed in the middle of readout.", ts_path.c_str());
} else {
diff --git a/guest/services/wifi/init.wifi.sh b/guest/services/wifi/init.wifi.sh
new file mode 100755
index 0000000..a23f174
--- /dev/null
+++ b/guest/services/wifi/init.wifi.sh
@@ -0,0 +1,7 @@
+#!/vendor/bin/sh
+
+wifi_mac_prefix=`getprop ro.boot.wifi_mac_prefix`
+if [ -n "$wifi_mac_prefix" ]; then
+ /vendor/bin/mac80211_create_radios 2 $wifi_mac_prefix || exit 1
+fi
+
diff --git a/host/commands/assemble_cvd/Android.bp b/host/commands/assemble_cvd/Android.bp
index 4ed4452..4afa9de 100644
--- a/host/commands/assemble_cvd/Android.bp
+++ b/host/commands/assemble_cvd/Android.bp
@@ -25,8 +25,10 @@
"boot_config.cc",
"boot_image_utils.cc",
"clean.cc",
+ "config.cpp",
"disk_flags.cc",
"flags.cc",
+ "flag_feature.cpp",
"misc_info.cc",
"super_image_mixer.cc",
],
@@ -37,6 +39,7 @@
"libcuttlefish_fs",
"libcuttlefish_utils",
"libbase",
+ "libfruit",
"libjsoncpp",
"libnl",
"libprotobuf-cpp-full",
diff --git a/host/commands/assemble_cvd/assemble_cvd.cc b/host/commands/assemble_cvd/assemble_cvd.cc
index c249473..0eabd42 100644
--- a/host/commands/assemble_cvd/assemble_cvd.cc
+++ b/host/commands/assemble_cvd/assemble_cvd.cc
@@ -23,10 +23,12 @@
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
+#include "common/libs/utils/flag_parser.h"
#include "common/libs/utils/tee_logging.h"
#include "host/commands/assemble_cvd/clean.h"
#include "host/commands/assemble_cvd/disk_flags.h"
#include "host/commands/assemble_cvd/flags.h"
+#include "host/libs/config/adb_config.h"
#include "host/libs/config/fetcher_config.h"
using cuttlefish::StringFromEnv;
@@ -51,8 +53,7 @@
FetcherConfig FindFetcherConfig(const std::vector<std::string>& files) {
FetcherConfig fetcher_config;
for (const auto& file : files) {
- auto expected_pos = file.size() - kFetcherConfigFile.size();
- if (file.rfind(kFetcherConfigFile) == expected_pos) {
+ if (android::base::EndsWith(file, kFetcherConfigFile)) {
if (fetcher_config.LoadFromFile(file)) {
return fetcher_config;
}
@@ -90,7 +91,9 @@
}
void ValidateAdbModeFlag(const CuttlefishConfig& config) {
- auto adb_modes = config.adb_mode();
+ AdbConfig adb_config;
+ CHECK(config.LoadFragment(adb_config)) << "No adb fragment";
+ auto adb_modes = adb_config.adb_mode();
adb_modes.erase(AdbMode::Unknown);
if (adb_modes.size() < 1) {
LOG(INFO) << "ADB not enabled";
@@ -133,11 +136,12 @@
auto config = InitializeCuttlefishConfiguration(
FLAGS_instance_dir, FLAGS_modem_simulator_count, kernel_config);
std::set<std::string> preserving;
- if (FLAGS_resume && ShouldCreateAllCompositeDisks(config)) {
+ bool create_os_composite_disk = ShouldCreateOsCompositeDisk(config);
+ if (FLAGS_resume && create_os_composite_disk) {
LOG(INFO) << "Requested resuming a previous session (the default behavior) "
<< "but the base images have changed under the overlay, making the "
<< "overlay incompatible. Wiping the overlay files.";
- } else if (FLAGS_resume && !ShouldCreateAllCompositeDisks(config)) {
+ } else if (FLAGS_resume && !create_os_composite_disk) {
preserving.insert("overlay.img");
preserving.insert("os_composite_disk_config.txt");
preserving.insert("os_composite_gpt_header.img");
@@ -248,7 +252,9 @@
ExtractKernelParamsFromFetcherConfig(fetcher_config);
KernelConfig kernel_config;
- CHECK(ParseCommandLineFlags(&argc, &argv, &kernel_config)) << "Failed to parse arguments";
+ auto flags = ArgsToVec(argc - 1, argv + 1);
+ CHECK(ParseCommandLineFlags(flags, &kernel_config))
+ << "Failed to parse arguments";
auto config =
InitFilesystemAndCreateConfig(std::move(fetcher_config), kernel_config);
diff --git a/host/commands/assemble_cvd/boot_config.cc b/host/commands/assemble_cvd/boot_config.cc
index b433940..33d24f4 100644
--- a/host/commands/assemble_cvd/boot_config.cc
+++ b/host/commands/assemble_cvd/boot_config.cc
@@ -29,6 +29,7 @@
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
#include "common/libs/utils/subprocess.h"
+#include "host/libs/config/bootconfig_args.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/config/kernel_args.h"
#include "host/libs/vm_manager/crosvm_manager.h"
@@ -43,10 +44,10 @@
namespace {
size_t WriteEnvironment(const CuttlefishConfig& config,
- const std::vector<std::string>& kernel_args,
+ const std::string& kernel_args,
const std::string& env_path) {
std::ostringstream env;
- env << "bootargs=" << android::base::Join(kernel_args, " ") << '\0';
+ env << "bootargs=" << kernel_args << '\0';
if (!config.boot_slot().empty()) {
env << "android_slot_suffix=_" << config.boot_slot() << '\0';
}
@@ -85,8 +86,29 @@
auto boot_env_image_path = instance.uboot_env_image_path();
auto tmp_boot_env_image_path = boot_env_image_path + ".tmp";
auto uboot_env_path = instance.PerInstancePath("mkenvimg_input");
- auto kernel_args = KernelCommandLineFromConfig(config);
- if(!WriteEnvironment(config, kernel_args, uboot_env_path)) {
+ auto kernel_cmdline =
+ android::base::Join(KernelCommandLineFromConfig(config), " ");
+ // If the bootconfig isn't supported in the guest kernel, the bootconfig args
+ // need to be passed in via the uboot env. This won't be an issue for protect
+ // kvm which is running a kernel with bootconfig support.
+ if (!config.bootconfig_supported()) {
+ auto bootconfig_args =
+ android::base::Join(BootconfigArgsFromConfig(config, instance), " ");
+ // "androidboot.hardware" kernel parameter has changed to "hardware" in
+ // bootconfig and needs to be replaced before being used in the kernel
+ // cmdline.
+ bootconfig_args = android::base::StringReplace(
+ bootconfig_args, " hardware=", " androidboot.hardware=", true);
+ // TODO(b/182417593): Until we pass the module parameters through
+ // modules.options, we pass them through bootconfig using
+ // 'kernel.<key>=<value>' But if we don't support bootconfig, we need to
+ // rename them back to the old cmdline version
+ bootconfig_args =
+ android::base::StringReplace(bootconfig_args, " kernel.", " ", true);
+ kernel_cmdline += " ";
+ kernel_cmdline += bootconfig_args;
+ }
+ if (!WriteEnvironment(config, kernel_cmdline, uboot_env_path)) {
LOG(ERROR) << "Unable to write out plaintext env '" << uboot_env_path << ".'";
return false;
}
diff --git a/host/commands/assemble_cvd/boot_image_utils.cc b/host/commands/assemble_cvd/boot_image_utils.cc
index 7b463d1..1483d9e 100644
--- a/host/commands/assemble_cvd/boot_image_utils.cc
+++ b/host/commands/assemble_cvd/boot_image_utils.cc
@@ -71,16 +71,6 @@
return true;
}
-std::string FindCpio() {
- for (const auto& path : {"/usr/bin/cpio", "/bin/cpio"}) {
- if (FileExists(path)) {
- return path;
- }
- }
- LOG(FATAL) << "Could not find a cpio executable.";
- return "";
-}
-
bool UnpackBootImage(const std::string& boot_image_path,
const std::string& unpack_dir) {
auto unpack_path = HostBinaryPath("unpack_bootimg");
@@ -111,30 +101,33 @@
const std::string& original_ramdisk_path,
const std::string& new_ramdisk_path,
const std::string& build_dir) {
- const auto& cpio_path = FindCpio();
int success = execute({"/bin/bash", "-c", HostBinaryPath("lz4") + " -c -d -l " +
original_ramdisk_path + " > " + original_ramdisk_path + CPIO_EXT});
CHECK(success == 0) << "Unable to run lz4. Exited with status " << success;
- success = mkdir((build_dir + "/" + TMP_RD_DIR).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
- CHECK(success == 0) << "Could not mkdir \"" << TMP_RD_DIR << "\", error was " << strerror(errno);
+ const std::string ramdisk_stage_dir = build_dir + "/" + TMP_RD_DIR;
+ success =
+ mkdir(ramdisk_stage_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ CHECK(success == 0) << "Could not mkdir \"" << ramdisk_stage_dir
+ << "\", error was " << strerror(errno);
- success = execute({"/bin/bash", "-c",
- "(cd " + build_dir + "/" + TMP_RD_DIR + " && (while " +
- cpio_path + " -id ; do :; done) < " +
- original_ramdisk_path + CPIO_EXT + ")"});
- CHECK(success == 0) << "Unable to run cd or cpio. Exited with status " << success;
+ success = execute(
+ {"/bin/bash", "-c",
+ "(cd " + ramdisk_stage_dir + " && while " + HostBinaryPath("toybox") +
+ " cpio -idu; do :; done) < " + original_ramdisk_path + CPIO_EXT});
+ CHECK(success == 0) << "Unable to run cd or cpio. Exited with status "
+ << success;
- success = execute({"/bin/bash", "-c", "rm -rf " + build_dir + "/" + TMP_RD_DIR + "/lib/modules"});
- CHECK(success == 0) << "Could not rmdir \"lib/modules\" in TMP_RD_DIR. Exited with status "
- << success;
+ success = execute({"rm", "-rf", ramdisk_stage_dir + "/lib/modules"});
+ CHECK(success == 0) << "Could not rmdir \"lib/modules\" in TMP_RD_DIR. "
+ << "Exited with status " << success;
const std::string stripped_ramdisk_path = build_dir + "/" + STRIPPED_RD;
success = execute({"/bin/bash", "-c",
- "(cd " + build_dir + "/" + TMP_RD_DIR + " && find . | " +
- cpio_path + " -H newc -o --quiet > " +
- stripped_ramdisk_path + CPIO_EXT + ")"});
- CHECK(success == 0) << "Unable to run cd or cpio. Exited with status " << success;
+ HostBinaryPath("mkbootfs") + " " + ramdisk_stage_dir +
+ " > " + stripped_ramdisk_path + CPIO_EXT});
+ CHECK(success == 0) << "Unable to run cd or cpio. Exited with status "
+ << success;
success = execute({"/bin/bash", "-c", HostBinaryPath("lz4") +
" -c -l -12 --favor-decSpeed " + stripped_ramdisk_path + CPIO_EXT + " > " +
@@ -242,20 +235,12 @@
const std::string& vendor_boot_image_path,
const std::string& new_vendor_boot_image_path,
const std::string& unpack_dir,
- const std::string& repack_dir,
- const std::vector<std::string>& bootconfig_args,
bool bootconfig_supported) {
if (UnpackVendorBootImageIfNotUnpacked(vendor_boot_image_path, unpack_dir) ==
false) {
return false;
}
- // TODO(b/173134558)
- // The vendor boot generation below isn't deterministic. i.e. running the same vendor boot
- // repack function twice with the same inputs will produce two differing vendor boot images.
- // This is because the vendor boot ramdisk contains a few symlinks. These symlinks affect the
- // ramdisk regeneration process and cause differing outputs each time (I still haven't figured
- // out why).
std::string ramdisk_path;
if (new_ramdisk.size()) {
ramdisk_path = unpack_dir + "/vendor_ramdisk_repacked";
@@ -268,17 +253,9 @@
ramdisk_path = unpack_dir + "/" + CONCATENATED_VENDOR_RAMDISK;
}
- auto bootconfig_fd = SharedFD::Creat(repack_dir + "/bootconfig", 0666);
- if (!bootconfig_fd->IsOpen()) {
- LOG(ERROR) << "Unable to create intermediate bootconfig file: "
- << bootconfig_fd->StrError();
- return false;
- }
- std::string bootconfig = ReadFile(unpack_dir + "/bootconfig") +
- android::base::Join(bootconfig_args, "\n") + "\n";
- bootconfig_fd->Write(bootconfig.c_str(), bootconfig.size());
- LOG(DEBUG) << "Bootconfig parameters from vendor boot image and config are "
- << ReadFile(repack_dir + "/bootconfig");
+ std::string bootconfig = ReadFile(unpack_dir + "/bootconfig");
+ LOG(DEBUG) << "Bootconfig parameters from vendor boot image are "
+ << bootconfig;
std::string vendor_boot_params = ReadFile(unpack_dir + "/vendor_boot_params");
auto kernel_cmdline =
ExtractValue(vendor_boot_params, "vendor command line args: ") +
@@ -286,11 +263,6 @@
? ""
: " " + android::base::StringReplace(bootconfig, "\n", " ", true));
if (!bootconfig_supported) {
- // "androidboot.hardware" kernel parameter has changed to "hardware" in
- // bootconfig and needs to be replaced before being used in the kernel
- // cmdline.
- kernel_cmdline = android::base::StringReplace(
- kernel_cmdline, " hardware=", " androidboot.hardware=", true);
// TODO(b/182417593): Until we pass the module parameters through
// modules.options, we pass them through bootconfig using
// 'kernel.<key>=<value>' But if we don't support bootconfig, we need to
@@ -315,7 +287,7 @@
repack_cmd.AddParameter(unpack_dir + "/dtb");
if (bootconfig_supported) {
repack_cmd.AddParameter("--vendor_bootconfig");
- repack_cmd.AddParameter(repack_dir + "/bootconfig");
+ repack_cmd.AddParameter(unpack_dir + "/bootconfig");
}
int success = repack_cmd.Start().Wait();
@@ -336,14 +308,11 @@
bool RepackVendorBootImageWithEmptyRamdisk(
const std::string& vendor_boot_image_path,
const std::string& new_vendor_boot_image_path,
- const std::string& unpack_dir, const std::string& repack_dir,
- const std::vector<std::string>& bootconfig_args,
- bool bootconfig_supported) {
+ const std::string& unpack_dir, bool bootconfig_supported) {
auto empty_ramdisk_file =
SharedFD::Creat(unpack_dir + "/empty_ramdisk", 0666);
return RepackVendorBootImage(
unpack_dir + "/empty_ramdisk", vendor_boot_image_path,
- new_vendor_boot_image_path, unpack_dir, repack_dir, bootconfig_args,
- bootconfig_supported);
+ new_vendor_boot_image_path, unpack_dir, bootconfig_supported);
}
} // namespace cuttlefish
diff --git a/host/commands/assemble_cvd/boot_image_utils.h b/host/commands/assemble_cvd/boot_image_utils.h
index fafb514..298fdb4 100644
--- a/host/commands/assemble_cvd/boot_image_utils.h
+++ b/host/commands/assemble_cvd/boot_image_utils.h
@@ -27,12 +27,9 @@
const std::string& vendor_boot_image_path,
const std::string& new_vendor_boot_image_path,
const std::string& unpack_dir,
- const std::string& repack_dir,
- const std::vector<std::string>& bootconfig_args,
bool bootconfig_supported);
bool RepackVendorBootImageWithEmptyRamdisk(
const std::string& vendor_boot_image_path,
const std::string& new_vendor_boot_image_path,
- const std::string& unpack_dir, const std::string& repack_dir,
- const std::vector<std::string>& bootconfig_args, bool bootconfig_supported);
+ const std::string& unpack_dir, bool bootconfig_supported);
}
diff --git a/host/commands/assemble_cvd/config.cpp b/host/commands/assemble_cvd/config.cpp
new file mode 100644
index 0000000..bb8ff38
--- /dev/null
+++ b/host/commands/assemble_cvd/config.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/commands/assemble_cvd/config.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <gflags/gflags.h>
+#include <json/json.h>
+#include <fstream>
+#include <set>
+#include <string>
+
+#include "common/libs/utils/files.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+using google::FlagSettingMode::SET_FLAGS_DEFAULT;
+
+DECLARE_string(system_image_dir);
+DEFINE_string(config, "phone",
+ "Config preset name. Will automatically set flag fields "
+ "using the values from this file of presets. See "
+ "device/google/cuttlefish/shared/config/config_*.json "
+ "for possible values.");
+
+namespace cuttlefish {
+namespace {
+
+bool IsFlagSet(const std::string& flag) {
+ return !gflags::GetCommandLineFlagInfoOrDie(flag.c_str()).is_default;
+}
+
+} // namespace
+
+void SetDefaultFlagsFromConfigPreset() {
+ std::string config_preset = FLAGS_config; // The name of the preset config.
+ std::string config_file_path; // The path to the preset config JSON.
+ std::set<std::string> allowed_config_presets;
+ for (const std::string& file :
+ DirectoryContents(DefaultHostArtifactsPath("etc/cvd_config"))) {
+ std::string_view local_file(file);
+ if (android::base::ConsumePrefix(&local_file, "cvd_config_") &&
+ android::base::ConsumeSuffix(&local_file, ".json")) {
+ allowed_config_presets.emplace(local_file);
+ }
+ }
+
+ // If the user specifies a --config name, then use that config
+ // preset option.
+ std::string android_info_path = FLAGS_system_image_dir + "/android-info.txt";
+ if (IsFlagSet("config")) {
+ if (!allowed_config_presets.count(config_preset)) {
+ LOG(FATAL) << "Invalid --config option '" << config_preset
+ << "'. Valid options: "
+ << android::base::Join(allowed_config_presets, ",");
+ }
+ } else if (FileExists(android_info_path)) {
+ // Otherwise try to load the correct preset using android-info.txt.
+ std::ifstream ifs(android_info_path);
+ if (ifs.is_open()) {
+ std::string android_info;
+ ifs >> android_info;
+ std::string_view local_android_info(android_info);
+ if (android::base::ConsumePrefix(&local_android_info, "config=")) {
+ config_preset = local_android_info;
+ }
+ if (!allowed_config_presets.count(config_preset)) {
+ LOG(WARNING) << android_info_path
+ << " contains invalid config preset: '"
+ << local_android_info << "'. Defaulting to 'phone'.";
+ config_preset = "phone";
+ }
+ }
+ }
+ LOG(INFO) << "Launching CVD using --config='" << config_preset << "'.";
+
+ config_file_path = DefaultHostArtifactsPath("etc/cvd_config/cvd_config_" +
+ config_preset + ".json");
+ Json::Value config;
+ Json::CharReaderBuilder builder;
+ std::ifstream ifs(config_file_path);
+ std::string errorMessage;
+ if (!Json::parseFromStream(builder, ifs, &config, &errorMessage)) {
+ LOG(FATAL) << "Could not read config file " << config_file_path << ": "
+ << errorMessage;
+ }
+ for (const std::string& flag : config.getMemberNames()) {
+ std::string value;
+ if (flag == "custom_actions") {
+ Json::StreamWriterBuilder factory;
+ value = Json::writeString(factory, config[flag]);
+ } else {
+ value = config[flag].asString();
+ }
+ if (gflags::SetCommandLineOptionWithMode(flag.c_str(), value.c_str(),
+ SET_FLAGS_DEFAULT)
+ .empty()) {
+ LOG(FATAL) << "Error setting flag '" << flag << "'.";
+ }
+ }
+}
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/size_utils.cpp b/host/commands/assemble_cvd/config.h
similarity index 66%
copy from common/libs/utils/size_utils.cpp
copy to host/commands/assemble_cvd/config.h
index 9f25445..ae46bbd 100644
--- a/common/libs/utils/size_utils.cpp
+++ b/host/commands/assemble_cvd/config.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -13,16 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#include "common/libs/utils/size_utils.h"
-
-#include <unistd.h>
+#pragma once
namespace cuttlefish {
-uint64_t AlignToPowerOf2(uint64_t val, uint8_t align_log) {
- uint64_t align = 1ULL << align_log;
- return ((val + (align - 1)) / align) * align;
-}
+void SetDefaultFlagsFromConfigPreset();
-} // namespace cuttlefish
+}
diff --git a/host/commands/assemble_cvd/disk_flags.cc b/host/commands/assemble_cvd/disk_flags.cc
index 67e278f..768311e 100644
--- a/host/commands/assemble_cvd/disk_flags.cc
+++ b/host/commands/assemble_cvd/disk_flags.cc
@@ -21,10 +21,13 @@
#include <fstream>
#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <gflags/gflags.h>
+#include "common/libs/fs/shared_buf.h"
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
+#include "common/libs/utils/size_utils.h"
#include "common/libs/utils/subprocess.h"
#include "host/commands/assemble_cvd/boot_config.h"
#include "host/commands/assemble_cvd/boot_image_utils.h"
@@ -130,8 +133,7 @@
return true;
}
-std::vector<ImagePartition> os_composite_disk_config(
- const CuttlefishConfig::InstanceSpecific& instance) {
+std::vector<ImagePartition> os_composite_disk_config() {
std::vector<ImagePartition> partitions;
partitions.push_back(ImagePartition {
.label = "misc",
@@ -145,27 +147,14 @@
.label = "boot_b",
.image_file_path = FLAGS_boot_image,
});
- // Boot image repacking is not supported on protected VMs. Repacking requires
- // resigning the image and keys on android hosts aren't trusted.
- if (!FLAGS_protected_vm) {
- partitions.push_back(ImagePartition{
- .label = "vendor_boot_a",
- .image_file_path = instance.vendor_boot_image_path(),
- });
- partitions.push_back(ImagePartition{
- .label = "vendor_boot_b",
- .image_file_path = instance.vendor_boot_image_path(),
- });
- } else {
- partitions.push_back(ImagePartition{
- .label = "vendor_boot_a",
- .image_file_path = FLAGS_vendor_boot_image,
- });
- partitions.push_back(ImagePartition{
- .label = "vendor_boot_b",
- .image_file_path = FLAGS_vendor_boot_image,
- });
- }
+ partitions.push_back(ImagePartition{
+ .label = "vendor_boot_a",
+ .image_file_path = FLAGS_vendor_boot_image,
+ });
+ partitions.push_back(ImagePartition{
+ .label = "vendor_boot_b",
+ .image_file_path = FLAGS_vendor_boot_image,
+ });
partitions.push_back(ImagePartition {
.label = "vbmeta_a",
.image_file_path = FLAGS_vbmeta_image,
@@ -225,6 +214,10 @@
.image_file_path = instance.factory_reset_protected_path(),
});
}
+ partitions.push_back(ImagePartition{
+ .label = "bootconfig",
+ .image_file_path = instance.persistent_bootconfig_path(),
+ });
return partitions;
}
@@ -244,31 +237,6 @@
return ret;
}
-bool ShouldCreateAllCompositeDisks(const CuttlefishConfig& config) {
- std::chrono::system_clock::time_point youngest_disk_img;
- for (auto& partition :
- os_composite_disk_config(config.ForDefaultInstance())) {
- auto partition_mod_time = FileModificationTime(partition.image_file_path);
- if (partition_mod_time > youngest_disk_img) {
- youngest_disk_img = partition_mod_time;
- }
- }
-
- // If the youngest partition img is younger than any composite disk, this fact implies that
- // the composite disks are all out of date and need to be reinitialized.
- for (auto& instance : config.Instances()) {
- if (!FileExists(instance.os_composite_disk_path())) {
- continue;
- }
- if (youngest_disk_img >
- FileModificationTime(instance.os_composite_disk_path())) {
- return true;
- }
- }
-
- return false;
-}
-
bool DoesCompositeMatchCurrentDiskConfig(
const std::string& prior_disk_config_path,
const std::vector<ImagePartition>& partitions) {
@@ -309,6 +277,11 @@
return composite_age < LastUpdatedInputDisk(partitions);
}
+bool ShouldCreateOsCompositeDisk(const CuttlefishConfig& config) {
+ return ShouldCreateCompositeDisk(config.os_composite_disk_path(),
+ os_composite_disk_config());
+}
+
static uint64_t AvailableSpaceAtPath(const std::string& path) {
struct statvfs vfs;
if (statvfs(path.c_str(), &vfs) != 0) {
@@ -321,12 +294,11 @@
return static_cast<uint64_t>(vfs.f_frsize) * vfs.f_bavail;
}
-bool CreateCompositeDisk(const CuttlefishConfig& config,
- const CuttlefishConfig::InstanceSpecific& instance) {
- if (!SharedFD::Open(instance.os_composite_disk_path().c_str(),
+bool CreateOsCompositeDisk(const CuttlefishConfig& config) {
+ if (!SharedFD::Open(config.os_composite_disk_path().c_str(),
O_WRONLY | O_CREAT, 0644)
->IsOpen()) {
- LOG(ERROR) << "Could not ensure " << instance.os_composite_disk_path()
+ LOG(ERROR) << "Could not ensure " << config.os_composite_disk_path()
<< " exists";
return false;
}
@@ -352,16 +324,15 @@
<< existing_sizes.disk_size;
}
std::string header_path =
- instance.PerInstancePath("os_composite_gpt_header.img");
+ config.AssemblyPath("os_composite_gpt_header.img");
std::string footer_path =
- instance.PerInstancePath("os_composite_gpt_footer.img");
- CreateCompositeDisk(os_composite_disk_config(instance), header_path,
- footer_path, instance.os_composite_disk_path());
+ config.AssemblyPath("os_composite_gpt_footer.img");
+ CreateCompositeDisk(os_composite_disk_config(), header_path, footer_path,
+ config.os_composite_disk_path());
} else {
// If this doesn't fit into the disk, it will fail while aggregating. The
// aggregator doesn't maintain any sparse attributes.
- AggregateImage(os_composite_disk_config(instance),
- instance.os_composite_disk_path());
+ AggregateImage(os_composite_disk_config(), config.os_composite_disk_path());
}
return true;
}
@@ -407,44 +378,69 @@
google::FlagSettingMode::SET_FLAGS_DEFAULT);
}
- for (auto instance : config->Instances()) {
+ if (FLAGS_kernel_path.size() || FLAGS_initramfs_path.size()) {
const std::string new_vendor_boot_image_path =
- instance.vendor_boot_image_path();
- const std::vector<std::string> boot_config_vector =
- BootconfigArgsFromConfig(*config, instance);
- if (FLAGS_kernel_path.size() || FLAGS_initramfs_path.size()) {
- // Repack the vendor boot images if kernels and/or ramdisks are passed in.
- if (FLAGS_initramfs_path.size()) {
- bool success = RepackVendorBootImage(
- FLAGS_initramfs_path, FLAGS_vendor_boot_image,
- new_vendor_boot_image_path, config->assembly_dir(),
- instance.instance_dir(), boot_config_vector,
- config->bootconfig_supported());
- CHECK(success) << "Failed to regenerate the vendor boot image with the "
- "new ramdisk";
- } else {
- // This control flow implies a kernel with all configs built in.
- // If it's just the kernel, repack the vendor boot image without a
- // ramdisk.
- bool success = RepackVendorBootImageWithEmptyRamdisk(
- FLAGS_vendor_boot_image, new_vendor_boot_image_path,
- config->assembly_dir(), instance.instance_dir(), boot_config_vector,
- config->bootconfig_supported());
- CHECK(success)
- << "Failed to regenerate the vendor boot image without a ramdisk";
- }
- } else {
- // Repack the vendor boot image to add the instance specific bootconfig
- // parameters
+ config->AssemblyPath("vendor_boot_repacked.img");
+ // Repack the vendor boot images if kernels and/or ramdisks are passed in.
+ if (FLAGS_initramfs_path.size()) {
bool success = RepackVendorBootImage(
- std::string(), FLAGS_vendor_boot_image, new_vendor_boot_image_path,
- config->assembly_dir(), instance.instance_dir(), boot_config_vector,
+ FLAGS_initramfs_path, FLAGS_vendor_boot_image,
+ new_vendor_boot_image_path, config->assembly_dir(),
config->bootconfig_supported());
- CHECK(success) << "Failed to regenerate the vendor boot image";
+ CHECK(success) << "Failed to regenerate the vendor boot image with the "
+ "new ramdisk";
+ } else {
+ // This control flow implies a kernel with all configs built in.
+ // If it's just the kernel, repack the vendor boot image without a
+ // ramdisk.
+ bool success = RepackVendorBootImageWithEmptyRamdisk(
+ FLAGS_vendor_boot_image, new_vendor_boot_image_path,
+ config->assembly_dir(), config->bootconfig_supported());
+ CHECK(success)
+ << "Failed to regenerate the vendor boot image without a ramdisk";
}
+ SetCommandLineOptionWithMode("vendor_boot_image",
+ new_vendor_boot_image_path.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
}
}
+static void GeneratePersistentBootconfig(
+ const CuttlefishConfig* config,
+ const CuttlefishConfig::InstanceSpecific& instance) {
+ const auto bootconfig_path = instance.persistent_bootconfig_path();
+ if (!FileExists(bootconfig_path)) {
+ CreateBlankImage(bootconfig_path, 1 /* mb */, "none");
+ }
+
+ auto bootconfig_fd = SharedFD::Open(bootconfig_path, O_RDWR);
+ CHECK(bootconfig_fd->IsOpen())
+ << "Unable to open bootconfig file: " << bootconfig_fd->StrError();
+
+ // Cuttlefish for the time being won't be able to support OTA from a
+ // non-bootconfig kernel to a bootconfig-kernel (or vice versa) IF the device
+ // is stopped (via stop_cvd). This is rarely an issue since OTA testing run
+ // on cuttlefish is done within one launch cycle of the device. If this ever
+ // becomes an issue, this code will have to be rewritten.
+ if (!config->bootconfig_supported()) {
+ return;
+ }
+
+ const std::string bootconfig =
+ android::base::Join(BootconfigArgsFromConfig(*config, instance), "\n") +
+ "\n";
+ ssize_t bytesWritten = WriteAll(bootconfig_fd, bootconfig);
+ CHECK(bytesWritten == bootconfig.size());
+ LOG(DEBUG) << "Bootconfig parameters from vendor boot image and config are "
+ << ReadFile(bootconfig_path);
+
+ const off_t bootconfig_size_bytes =
+ AlignToPowerOf2(bootconfig.size(), PARTITION_SIZE_SHIFT);
+ CHECK(bootconfig_fd->Truncate(bootconfig_size_bytes) == 0)
+ << "`truncate --size=" << bootconfig_size_bytes << " bytes "
+ << bootconfig_path << "` failed:" << bootconfig_fd->StrError();
+}
+
void CreateDynamicDiskFiles(const FetcherConfig& fetcher_config,
const CuttlefishConfig* config) {
// Create misc if necessary
@@ -474,9 +470,10 @@
RepackAllBootImages(config);
for (const auto& instance : config->Instances()) {
- if (!FileExists(instance.access_kregistry_path())) {
- CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none");
- }
+ // TODO(162770965) Re-enable once QEMU on GCE supports virtio pci pmem devices
+ // if (!FileExists(instance.access_kregistry_path())) {
+ // CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none");
+ // }
if (!FileExists(instance.pstore_path())) {
CreateBlankImage(instance.pstore_path(), 2 /* mb */, "none");
@@ -494,6 +491,8 @@
if (!FileExists(frp)) {
CreateBlankImage(frp, 1 /* mb */, "none");
}
+
+ GeneratePersistentBootconfig(config, instance);
}
}
@@ -530,26 +529,27 @@
CHECK(success) << "Super image rebuilding requested but could not be completed.";
}
+ bool oldOsCompositeDisk = ShouldCreateOsCompositeDisk(*config);
bool newDataImage = dataImageResult == DataImageResult::FileUpdated;
+ bool osCompositeMatchesDiskConfig = DoesCompositeMatchCurrentDiskConfig(
+ config->AssemblyPath("os_composite_disk_config.txt"),
+ os_composite_disk_config());
+ if (!osCompositeMatchesDiskConfig || oldOsCompositeDisk || !FLAGS_resume ||
+ newDataImage) {
+ CHECK(CreateOsCompositeDisk(*config))
+ << "Failed to create OS composite disk";
- for (auto instance : config->Instances()) {
- bool compositeMatchesDiskConfig = DoesCompositeMatchCurrentDiskConfig(
- instance.PerInstancePath("os_composite_disk_config.txt"),
- os_composite_disk_config(instance));
- bool oldCompositeDisk = ShouldCreateCompositeDisk(
- instance.os_composite_disk_path(), os_composite_disk_config(instance));
- if (!compositeMatchesDiskConfig || oldCompositeDisk || !FLAGS_resume || newDataImage) {
+ for (auto instance : config->Instances()) {
if (FLAGS_resume) {
LOG(INFO) << "Requested to continue an existing session, (the default) "
<< "but the disk files have become out of date. Wiping the "
<< "old session files and starting a new session for device "
<< instance.serial_number();
}
- CHECK(CreateCompositeDisk(*config, instance))
- << "Failed to create composite disk";
- if (FileExists(instance.access_kregistry_path())) {
- CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none");
- }
+ // TODO(162770965) Re-enable once QEMU on GCE supports virtio pci pmem devices
+ // if (FileExists(instance.access_kregistry_path())) {
+ // CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none");
+ // }
if (FileExists(instance.pstore_path())) {
CreateBlankImage(instance.pstore_path(), 2 /* mb */, "none");
}
@@ -561,10 +561,10 @@
auto overlay_path = instance.PerInstancePath("overlay.img");
bool missingOverlay = !FileExists(overlay_path);
bool newOverlay = FileModificationTime(overlay_path) <
- FileModificationTime(instance.os_composite_disk_path());
+ FileModificationTime(config->os_composite_disk_path());
if (missingOverlay || !FLAGS_resume || newOverlay) {
CreateQcowOverlay(config->crosvm_binary(),
- instance.os_composite_disk_path(), overlay_path);
+ config->os_composite_disk_path(), overlay_path);
}
}
}
diff --git a/host/commands/assemble_cvd/disk_flags.h b/host/commands/assemble_cvd/disk_flags.h
index 3491c3a..1fc2d32 100644
--- a/host/commands/assemble_cvd/disk_flags.h
+++ b/host/commands/assemble_cvd/disk_flags.h
@@ -26,7 +26,7 @@
namespace cuttlefish {
bool ResolveInstanceFiles();
-bool ShouldCreateAllCompositeDisks(const CuttlefishConfig& config);
+bool ShouldCreateOsCompositeDisk(const CuttlefishConfig& config);
void CreateDynamicDiskFiles(const FetcherConfig& fetcher_config,
const CuttlefishConfig* config);
diff --git a/host/commands/assemble_cvd/flag_feature.cpp b/host/commands/assemble_cvd/flag_feature.cpp
new file mode 100644
index 0000000..f84a1af
--- /dev/null
+++ b/host/commands/assemble_cvd/flag_feature.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/commands/assemble_cvd/flag_feature.h"
+
+#include <android-base/strings.h>
+#include <fruit/fruit.h>
+#include <gflags/gflags.h>
+#include <string.h>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "host/libs/config/feature.h"
+
+namespace cuttlefish {
+
+static std::string XmlEscape(const std::string& s) {
+ using android::base::StringReplace;
+ return StringReplace(StringReplace(s, "<", "<", true), ">", ">", true);
+}
+
+class ParseGflagsImpl : public ParseGflags {
+ public:
+ INJECT(ParseGflagsImpl()) {}
+
+ std::string Name() const override { return "ParseGflags"; }
+ std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
+ bool Process(std::vector<std::string>& args) override {
+ std::string process_name = "assemble_cvd";
+ std::vector<char*> pseudo_argv = {process_name.data()};
+ for (auto& arg : args) {
+ pseudo_argv.push_back(arg.data());
+ }
+ int argc = pseudo_argv.size();
+ auto argv = pseudo_argv.data();
+ gflags::AllowCommandLineReparsing(); // Support future non-gflags flags
+ gflags::ParseCommandLineNonHelpFlags(&argc, &argv,
+ /* remove_flags */ false);
+ return true;
+ }
+ bool WriteGflagsCompatHelpXml(std::ostream& out) const override {
+ // Lifted from external/gflags/src/gflags_reporting.cc:ShowXMLOfFlags
+ std::vector<gflags::CommandLineFlagInfo> flags;
+ gflags::GetAllFlags(&flags);
+ for (const auto& flag : flags) {
+ // From external/gflags/src/gflags_reporting.cc:DescribeOneFlagInXML
+ out << "<flag>\n";
+ out << " <file>" << XmlEscape(flag.filename) << "</file>\n";
+ out << " <name>" << XmlEscape(flag.name) << "</name>\n";
+ out << " <meaning>" << XmlEscape(flag.description) << "</meaning>\n";
+ out << " <default>" << XmlEscape(flag.default_value) << "</default>\n";
+ out << " <current>" << XmlEscape(flag.current_value) << "</current>\n";
+ out << " <type>" << XmlEscape(flag.type) << "</type>\n";
+ out << "</flag>\n";
+ }
+ return true;
+ }
+};
+
+fruit::Component<ParseGflags> GflagsComponent() {
+ return fruit::createComponent()
+ .bind<ParseGflags, ParseGflagsImpl>()
+ .addMultibinding<FlagFeature, ParseGflags>();
+}
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/size_utils.cpp b/host/commands/assemble_cvd/flag_feature.h
similarity index 68%
copy from common/libs/utils/size_utils.cpp
copy to host/commands/assemble_cvd/flag_feature.h
index 9f25445..ab37e6c 100644
--- a/common/libs/utils/size_utils.cpp
+++ b/host/commands/assemble_cvd/flag_feature.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -13,16 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-#include "common/libs/utils/size_utils.h"
+#include <fruit/fruit.h>
-#include <unistd.h>
+#include "host/commands/assemble_cvd/config.h"
+#include "host/libs/config/feature.h"
namespace cuttlefish {
-uint64_t AlignToPowerOf2(uint64_t val, uint8_t align_log) {
- uint64_t align = 1ULL << align_log;
- return ((val + (align - 1)) / align) * align;
-}
+class ParseGflags : public FlagFeature {};
+
+fruit::Component<ParseGflags> GflagsComponent();
} // namespace cuttlefish
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 2d3f9da..2e1e979 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -1,6 +1,7 @@
#include "host/commands/assemble_cvd/flags.h"
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <gflags/gflags.h>
#include <json/json.h>
@@ -16,13 +17,18 @@
#include <regex>
#include <set>
#include <sstream>
+#include <unordered_map>
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
+#include "common/libs/utils/flag_parser.h"
#include "host/commands/assemble_cvd/alloc.h"
#include "host/commands/assemble_cvd/boot_config.h"
#include "host/commands/assemble_cvd/clean.h"
+#include "host/commands/assemble_cvd/config.h"
#include "host/commands/assemble_cvd/disk_flags.h"
+#include "host/commands/assemble_cvd/flag_feature.h"
+#include "host/libs/config/adb_config.h"
#include "host/libs/config/host_tools_version.h"
#include "host/libs/graphics_detector/graphics_detector.h"
#include "host/libs/vm_manager/crosvm_manager.h"
@@ -35,12 +41,6 @@
using cuttlefish::vm_manager::CrosvmManager;
using google::FlagSettingMode::SET_FLAGS_DEFAULT;
-DEFINE_string(config, "phone",
- "Config preset name. Will automatically set flag fields "
- "using the values from this file of presets. See "
- "device/google/cuttlefish/shared/config/config_*.json "
- "for possible values.");
-
DEFINE_int32(cpus, 2, "Virtual CPU count.");
DEFINE_string(data_policy, "use_existing", "How to handle userdata partition."
" Either 'use_existing', 'create_if_missing', 'resize_up_to', or "
@@ -54,6 +54,26 @@
"kernel must have been built with CONFIG_RANDOMIZE_BASE "
"disabled.");
+constexpr const char kDisplayHelp[] =
+ "Comma separated key=value pairs of display properties. Supported "
+ "properties:\n"
+ " 'width': required, width of the display in pixels\n"
+ " 'height': required, height of the display in pixels\n"
+ " 'dpi': optional, default 320, density of the display\n"
+ " 'refresh_rate_hz': optional, default 60, display refresh rate in Hertz\n"
+ ". Example usage: \n"
+ "--display0=width=1280,height=720\n"
+ "--display1=width=1440,height=900,dpi=480,refresh_rate_hz=30\n";
+
+// TODO(b/192495477): combine these into a single repeatable '--display' flag
+// when assemble_cvd switches to using the new flag parsing library.
+DEFINE_string(display0, "", kDisplayHelp);
+DEFINE_string(display1, "", kDisplayHelp);
+DEFINE_string(display2, "", kDisplayHelp);
+DEFINE_string(display3, "", kDisplayHelp);
+
+// TODO(b/171305898): mark these as deprecated after multi-display is fully
+// enabled.
DEFINE_int32(x_res, 0, "Width of the screen in pixels");
DEFINE_int32(y_res, 0, "Height of the screen in pixels");
DEFINE_int32(dpi, 0, "Pixels per inch for the screen");
@@ -85,6 +105,7 @@
"The VNC server runs at port 6443 + i for "
"the vsoc-i user or CUTTLEFISH_INSTANCE=i, "
"starting from 1.");
+
DEFINE_bool(use_allocd, false,
"Acquire static resources from the resource allocator daemon.");
DEFINE_bool(enable_minimal_mode, false,
@@ -180,9 +201,13 @@
"The path section of the URL where the device should be "
"registered with the signaling server.");
+DEFINE_bool(webrtc_sig_server_secure, true,
+ "Whether the WebRTC signaling server uses secure protocols (WSS vs WS).");
+
DEFINE_bool(verify_sig_server_certificate, false,
"Whether to verify the signaling server's certificate with a "
- "trusted signing authority (Disallow self signed certificates).");
+ "trusted signing authority (Disallow self signed certificates). "
+ "This is ignored if an insecure server is configured.");
DEFINE_string(sig_server_headers_file, "",
"Path to a file containing HTTP headers to be included in the "
@@ -265,6 +290,11 @@
DEFINE_bool(vhost_net, false, "Enable vhost acceleration of networking");
+DEFINE_string(vhost_user_mac80211_hwsim, "",
+ "Unix socket path for vhost-user of mac80211_hwsim");
+DEFINE_string(ap_rootfs_image, "", "rootfs image for AP instance");
+DEFINE_string(ap_kernel_image, "", "kernel image for AP instance");
+
DEFINE_bool(record_screen, false, "Enable screen recording. "
"Requires --start_webrtc");
@@ -299,6 +329,8 @@
DEFINE_bool(enable_audio, cuttlefish::HostArch() != cuttlefish::Arch::Arm64,
"Whether to play or capture audio");
+DEFINE_uint32(camera_server_port, 0, "camera vsock port");
+
DECLARE_string(assembly_dir);
DECLARE_string(boot_image);
DECLARE_string(system_image_dir);
@@ -309,10 +341,6 @@
namespace {
-bool IsFlagSet(const std::string& flag) {
- return !gflags::GetCommandLineFlagInfoOrDie(flag.c_str()).is_default;
-}
-
std::pair<uint16_t, uint16_t> ParsePortRange(const std::string& flag) {
static const std::regex rgx("[0-9]+:[0-9]+");
CHECK(std::regex_match(flag, rgx))
@@ -337,6 +365,60 @@
return stream.str();
}
+std::optional<CuttlefishConfig::DisplayConfig> ParseDisplayConfig(
+ const std::string& flag) {
+ if (flag.empty()) {
+ return std::nullopt;
+ }
+
+ std::unordered_map<std::string, std::string> props;
+
+ const std::vector<std::string> pairs = android::base::Split(flag, ",");
+ for (const std::string& pair : pairs) {
+ const std::vector<std::string> keyvalue = android::base::Split(pair, "=");
+ CHECK_EQ(2, keyvalue.size()) << "Invalid display: " << flag;
+
+ const std::string& prop_key = keyvalue[0];
+ const std::string& prop_val = keyvalue[1];
+ props[prop_key] = prop_val;
+ }
+
+ CHECK(props.find("width") != props.end())
+ << "Display configuration missing 'width' in " << flag;
+ CHECK(props.find("height") != props.end())
+ << "Display configuration missing 'height' in " << flag;
+
+ int display_width;
+ CHECK(android::base::ParseInt(props["width"], &display_width))
+ << "Display configuration invalid 'width' in " << flag;
+
+ int display_height;
+ CHECK(android::base::ParseInt(props["height"], &display_height))
+ << "Display configuration invalid 'height' in " << flag;
+
+ int display_dpi = 320;
+ auto display_dpi_it = props.find("dpi");
+ if (display_dpi_it != props.end()) {
+ CHECK(android::base::ParseInt(display_dpi_it->second, &display_dpi))
+ << "Display configuration invalid 'dpi' in " << flag;
+ }
+
+ int display_refresh_rate_hz = 60;
+ auto display_refresh_rate_hz_it = props.find("refresh_rate_hz");
+ if (display_refresh_rate_hz_it != props.end()) {
+ CHECK(android::base::ParseInt(display_refresh_rate_hz_it->second,
+ &display_refresh_rate_hz))
+ << "Display configuration invalid 'refresh_rate_hz' in " << flag;
+ }
+
+ return CuttlefishConfig::DisplayConfig{
+ .width = display_width,
+ .height = display_height,
+ .dpi = display_dpi,
+ .refresh_rate_hz = display_refresh_rate_hz,
+ };
+}
+
#ifdef __ANDROID__
void ReadKernelConfig(KernelConfig* kernel_config) {
// QEMU isn't on Android, so always follow host arch
@@ -408,6 +490,40 @@
}
tmp_config_obj.set_vm_manager(FLAGS_vm_manager);
+ std::vector<CuttlefishConfig::DisplayConfig> display_configs;
+
+ auto display0 = ParseDisplayConfig(FLAGS_display0);
+ if (display0) {
+ display_configs.push_back(*display0);
+ }
+ auto display1 = ParseDisplayConfig(FLAGS_display1);
+ if (display1) {
+ display_configs.push_back(*display1);
+ }
+ auto display2 = ParseDisplayConfig(FLAGS_display2);
+ if (display2) {
+ display_configs.push_back(*display2);
+ }
+ auto display3 = ParseDisplayConfig(FLAGS_display3);
+ if (display3) {
+ display_configs.push_back(*display3);
+ }
+
+ if (FLAGS_x_res > 0 && FLAGS_y_res > 0) {
+ if (display_configs.empty()) {
+ display_configs.push_back({
+ .width = FLAGS_x_res,
+ .height = FLAGS_y_res,
+ .dpi = FLAGS_dpi,
+ .refresh_rate_hz = FLAGS_refresh_rate_hz,
+ });
+ } else {
+ LOG(WARNING) << "Ignoring --x_res and --y_res when --displayN specified.";
+ }
+ }
+
+ tmp_config_obj.set_display_configs(display_configs);
+
const GraphicsAvailability graphics_availability =
GetGraphicsAvailabilityWithSubprocessCheck();
@@ -423,8 +539,8 @@
}
if (tmp_config_obj.gpu_mode() == kGpuModeAuto) {
if (ShouldEnableAcceleratedRendering(graphics_availability)) {
- LOG(INFO) << "GPU auto mode: detected prerequisites for accelerated "
- "rendering support.";
+ LOG(INFO) << "GPU auto mode: detected prerequisites for accelerated "
+ "rendering support.";
if (FLAGS_vm_manager == QemuManager::name()) {
LOG(INFO) << "Enabling --gpu_mode=drm_virgl.";
tmp_config_obj.set_gpu_mode(kGpuModeDrmVirgl);
@@ -469,22 +585,19 @@
tmp_config_obj.set_setupwizard_mode(FLAGS_setupwizard_mode);
- std::vector<cuttlefish::CuttlefishConfig::DisplayConfig> display_configs = {{
- .width = FLAGS_x_res,
- .height = FLAGS_y_res,
- }};
- tmp_config_obj.set_display_configs(display_configs);
- tmp_config_obj.set_dpi(FLAGS_dpi);
- tmp_config_obj.set_refresh_rate_hz(FLAGS_refresh_rate_hz);
-
auto secure_hals = android::base::Split(FLAGS_secure_hals, ",");
tmp_config_obj.set_secure_hals(
std::set<std::string>(secure_hals.begin(), secure_hals.end()));
tmp_config_obj.set_gdb_port(FLAGS_gdb_port);
- std::vector<std::string> adb = android::base::Split(FLAGS_adb_mode, ",");
- tmp_config_obj.set_adb_mode(std::set<std::string>(adb.begin(), adb.end()));
+ {
+ AdbConfig adb_config;
+ std::vector<std::string> adb = android::base::Split(FLAGS_adb_mode, ",");
+ adb_config.set_adb_mode(std::set<std::string>(adb.begin(), adb.end()));
+ adb_config.set_run_adb_connector(FLAGS_run_adb_connector);
+ CHECK(tmp_config_obj.SaveFragment(adb_config)) << "Failed to save adb info";
+ }
tmp_config_obj.set_guest_enforce_security(FLAGS_guest_enforce_security);
tmp_config_obj.set_guest_audit_security(FLAGS_guest_audit_security);
@@ -512,6 +625,7 @@
tmp_config_obj.set_enable_webrtc(FLAGS_start_webrtc);
tmp_config_obj.set_webrtc_assets_dir(FLAGS_webrtc_assets_dir);
tmp_config_obj.set_webrtc_certs_dir(FLAGS_webrtc_certs_dir);
+ tmp_config_obj.set_sig_server_secure(FLAGS_webrtc_sig_server_secure);
// Note: This will be overridden if the sig server is started by us
tmp_config_obj.set_sig_server_port(FLAGS_webrtc_sig_server_port);
tmp_config_obj.set_sig_server_address(FLAGS_webrtc_sig_server_addr);
@@ -533,7 +647,6 @@
FLAGS_webrtc_enable_adb_websocket);
tmp_config_obj.set_restart_subprocesses(FLAGS_restart_subprocesses);
- tmp_config_obj.set_run_adb_connector(FLAGS_run_adb_connector);
tmp_config_obj.set_run_as_daemon(FLAGS_daemon);
tmp_config_obj.set_data_policy(FLAGS_data_policy);
@@ -542,9 +655,8 @@
tmp_config_obj.set_enable_gnss_grpc_proxy(FLAGS_start_gnss_proxy);
- tmp_config_obj.set_enable_vehicle_hal_grpc_server(FLAGS_enable_vehicle_hal_grpc_server);
- tmp_config_obj.set_vehicle_hal_grpc_server_binary(
- HostBinaryPath("android.hardware.automotive.vehicle@2.0-virtualization-grpc-server"));
+ tmp_config_obj.set_enable_vehicle_hal_grpc_server(
+ FLAGS_enable_vehicle_hal_grpc_server);
std::string custom_action_config;
if (!FLAGS_custom_action_config.empty()) {
@@ -614,6 +726,20 @@
tmp_config_obj.set_vhost_net(FLAGS_vhost_net);
+ tmp_config_obj.set_vhost_user_mac80211_hwsim(FLAGS_vhost_user_mac80211_hwsim);
+
+ if ((FLAGS_ap_rootfs_image.empty()) != (FLAGS_ap_kernel_image.empty())) {
+ LOG(FATAL) << "Either both ap_rootfs_image and ap_kernel_image should be "
+ "set or neither should be set.";
+ } else if (FLAGS_vhost_user_mac80211_hwsim.empty() &&
+ !FLAGS_ap_rootfs_image.empty() && !FLAGS_ap_kernel_image.empty()) {
+ LOG(FATAL) << "To use external AP instance, vhost_user_mac80211_hwsim must "
+ "be set.";
+ }
+
+ tmp_config_obj.set_ap_rootfs_image(FLAGS_ap_rootfs_image);
+ tmp_config_obj.set_ap_kernel_image(FLAGS_ap_kernel_image);
+
tmp_config_obj.set_record_screen(FLAGS_record_screen);
tmp_config_obj.set_ethernet(FLAGS_ethernet);
@@ -670,9 +796,11 @@
instance.set_uuid(FLAGS_uuid);
+ instance.set_modem_simulator_host_id(1000 + num); // Must be 4 digits
instance.set_vnc_server_port(6444 + num - 1);
- instance.set_host_port(6520 + num - 1);
+ instance.set_adb_host_port(6520 + num - 1);
instance.set_adb_ip_and_port("0.0.0.0:" + std::to_string(6520 + num - 1));
+ instance.set_confui_host_vsock_port(7700 + num - 1);
instance.set_tombstone_receiver_port(calc_vsock_port(6600));
instance.set_vehicle_hal_server_port(9210 + num - 1);
instance.set_audiocontrol_server_port(9410); /* OK to use the same port number across instances */
@@ -701,6 +829,7 @@
instance.set_rootcanal_default_commands_file(
FLAGS_bluetooth_default_commands_file);
+ instance.set_camera_server_port(FLAGS_camera_server_port);
instance.set_device_title(FLAGS_device_title);
if (FLAGS_protected_vm) {
@@ -717,14 +846,7 @@
instance.set_virtual_disk_paths(virtual_disk_paths);
}
- std::array<unsigned char, 6> mac_address;
- mac_address[0] = 1 << 6; // locally administered
- // TODO(schuffelen): Randomize these and preserve the state.
- for (int i = 1; i < 5; i++) {
- mac_address[i] = i;
- }
- mac_address[5] = num;
- instance.set_wifi_mac_address(mac_address);
+ instance.set_wifi_mac_prefix(5554 + (num - 1) * 2);
instance.set_start_webrtc_signaling_server(false);
@@ -753,9 +875,12 @@
if (modem_simulator_count > 0) {
std::stringstream modem_ports;
for (auto index {0}; index < modem_simulator_count - 1; index++) {
- modem_ports << calc_vsock_port(9200) << ",";
+ auto port = 9200 + (modem_simulator_count * (num - 1)) + index;
+ modem_ports << calc_vsock_port(port) << ",";
}
- modem_ports << calc_vsock_port(9200);
+ auto port = 9200 + (modem_simulator_count * (num - 1)) +
+ modem_simulator_count - 1;
+ modem_ports << calc_vsock_port(port);
instance.set_modem_simulator_ports(modem_ports.str());
} else {
instance.set_modem_simulator_ports("");
@@ -776,75 +901,7 @@
return tmp_config_obj;
}
-void SetDefaultFlagsFromConfigPreset() {
- std::string config_preset = FLAGS_config; // The name of the preset config.
- std::string config_file_path; // The path to the preset config JSON.
- std::set<std::string> allowed_config_presets;
- for (const std::string& file :
- DirectoryContents(DefaultHostArtifactsPath("etc/cvd_config"))) {
- std::string_view local_file(file);
- if (android::base::ConsumePrefix(&local_file, "cvd_config_") &&
- android::base::ConsumeSuffix(&local_file, ".json")) {
- allowed_config_presets.emplace(local_file);
- }
- }
-
- // If the user specifies a --config name, then use that config
- // preset option.
- std::string android_info_path = FLAGS_system_image_dir + "/android-info.txt";
- if (IsFlagSet("config")) {
- if (!allowed_config_presets.count(config_preset)) {
- LOG(FATAL) << "Invalid --config option '" << config_preset
- << "'. Valid options: "
- << android::base::Join(allowed_config_presets, ",");
- }
- } else if (FileExists(android_info_path)) {
- // Otherwise try to load the correct preset using android-info.txt.
- std::ifstream ifs(android_info_path);
- if (ifs.is_open()) {
- std::string android_info;
- ifs >> android_info;
- std::string_view local_android_info(android_info);
- if (android::base::ConsumePrefix(&local_android_info, "config=")) {
- config_preset = local_android_info;
- }
- if (!allowed_config_presets.count(config_preset)) {
- LOG(WARNING) << android_info_path
- << " contains invalid config preset: '"
- << local_android_info << "'. Defaulting to 'phone'.";
- config_preset = "phone";
- }
- }
- }
- LOG(INFO) << "Launching CVD using --config='" << config_preset << "'.";
-
- config_file_path = DefaultHostArtifactsPath("etc/cvd_config/cvd_config_" +
- config_preset + ".json");
- Json::Value config;
- Json::CharReaderBuilder builder;
- std::ifstream ifs(config_file_path);
- std::string errorMessage;
- if (!Json::parseFromStream(builder, ifs, &config, &errorMessage)) {
- LOG(FATAL) << "Could not read config file " << config_file_path << ": "
- << errorMessage;
- }
- for (const std::string& flag : config.getMemberNames()) {
- std::string value;
- if (flag == "custom_actions") {
- Json::StreamWriterBuilder factory;
- value = Json::writeString(factory, config[flag]);
- } else {
- value = config[flag].asString();
- }
- if (gflags::SetCommandLineOptionWithMode(flag.c_str(), value.c_str(),
- SET_FLAGS_DEFAULT)
- .empty()) {
- LOG(FATAL) << "Error setting flag '" << flag << "'.";
- }
- }
-}
-
-void SetDefaultFlagsForQemu() {
+void SetDefaultFlagsForQemu(Arch target_arch) {
// for now, we don't set non-default options for QEMU
if (FLAGS_gpu_mode == kGpuModeGuestSwiftshader && NumStreamers() == 0) {
// This makes WebRTC the default streamer unless the user requests
@@ -852,7 +909,16 @@
// possible to run without any streamer by setting --start_webrtc=false.
SetCommandLineOptionWithMode("start_webrtc", "true", SET_FLAGS_DEFAULT);
}
- std::string default_bootloader = FLAGS_system_image_dir + "/bootloader.qemu";
+ std::string default_bootloader =
+ DefaultHostArtifactsPath("etc/bootloader_");
+ if(target_arch == Arch::Arm) {
+ default_bootloader += "arm";
+ } else if (target_arch == Arch::Arm64) {
+ default_bootloader += "aarch64";
+ } else {
+ default_bootloader += "x86_64";
+ }
+ default_bootloader += "/bootloader.qemu";
SetCommandLineOptionWithMode("bootloader", default_bootloader.c_str(),
SET_FLAGS_DEFAULT);
}
@@ -865,19 +931,11 @@
SetCommandLineOptionWithMode("start_webrtc", "true", SET_FLAGS_DEFAULT);
}
- // for now, we support only x86_64 by default
- bool default_enable_sandbox = false;
std::set<Arch> supported_archs{Arch::X86_64};
- if (supported_archs.find(HostArch()) != supported_archs.end()) {
- if (DirectoryExists(kCrosvmVarEmptyDir)) {
- default_enable_sandbox = IsDirectoryEmpty(kCrosvmVarEmptyDir);
- } else if (FileExists(kCrosvmVarEmptyDir)) {
- default_enable_sandbox = false;
- } else {
- default_enable_sandbox = EnsureDirectoryExists(kCrosvmVarEmptyDir);
- }
- }
-
+ bool default_enable_sandbox =
+ supported_archs.find(HostArch()) != supported_archs.end() &&
+ EnsureDirectoryExists(kCrosvmVarEmptyDir) &&
+ IsDirectoryEmpty(kCrosvmVarEmptyDir) && !IsRunningInContainer();
SetCommandLineOptionWithMode("enable_sandbox",
(default_enable_sandbox ? "true" : "false"),
SET_FLAGS_DEFAULT);
@@ -887,10 +945,55 @@
SET_FLAGS_DEFAULT);
}
-bool ParseCommandLineFlags(int* argc, char*** argv, KernelConfig* kernel_config) {
- google::ParseCommandLineNonHelpFlags(argc, argv, true);
+fruit::Component<> FlagsComponent() {
+ return fruit::createComponent().install(GflagsComponent);
+}
+
+bool ParseCommandLineFlags(std::vector<std::string>& args,
+ KernelConfig* kernel_config) {
+ bool help = false;
+ std::string help_str;
+ bool helpxml = false;
+
+ std::vector<Flag> help_flags = {
+ GflagsCompatFlag("help", help),
+ GflagsCompatFlag("helpfull", help),
+ GflagsCompatFlag("helpshort", help),
+ GflagsCompatFlag("helpmatch", help_str),
+ GflagsCompatFlag("helpon", help_str),
+ GflagsCompatFlag("helppackage", help_str),
+ GflagsCompatFlag("helpxml", helpxml),
+ };
+ for (const auto& help_flag : help_flags) {
+ if (!help_flag.Parse(args)) {
+ LOG(ERROR) << "Failed to process help flag.";
+ return false;
+ }
+ }
+
+ fruit::Injector<> injector(FlagsComponent);
+ auto flag_features = injector.getMultibindings<FlagFeature>();
+ if (!FlagFeature::ProcessFlags(flag_features, args)) {
+ LOG(ERROR) << "Failed to parse flags.";
+ return false;
+ }
+
SetDefaultFlagsFromConfigPreset();
- google::HandleCommandLineHelpFlags();
+
+ if (help || help_str != "") {
+ LOG(WARNING) << "TODO(schuffelen): Implement `--help` for assemble_cvd.";
+ LOG(WARNING) << "In the meantime, call `launch_cvd --help`";
+ return false;
+ } else if (helpxml) {
+ if (!FlagFeature::WriteGflagsHelpXml(flag_features, std::cout)) {
+ LOG(ERROR) << "Failure in writing gflags helpxml output";
+ }
+ std::exit(1); // For parity with gflags
+ }
+ // TODO(schuffelen): Put in "unknown flag" guards after gflags is removed.
+ // gflags either consumes all arguments that start with - or leaves all of
+ // them in place, and either errors out on unknown flags or accepts any flags.
+
bool invalid_manager = false;
if (!ResolveInstanceFiles()) {
@@ -907,7 +1010,7 @@
}
if (FLAGS_vm_manager == QemuManager::name()) {
- SetDefaultFlagsForQemu();
+ SetDefaultFlagsForQemu(kernel_config->target_arch);
} else if (FLAGS_vm_manager == CrosvmManager::name()) {
SetDefaultFlagsForCrosvm();
} else {
diff --git a/host/commands/assemble_cvd/flags.h b/host/commands/assemble_cvd/flags.h
index 3c02d92..70be3a1 100644
--- a/host/commands/assemble_cvd/flags.h
+++ b/host/commands/assemble_cvd/flags.h
@@ -2,6 +2,8 @@
#include <cstdint>
#include <optional>
+#include <string>
+#include <vector>
#include "common/libs/utils/environment.h"
#include "host/libs/config/cuttlefish_config.h"
@@ -14,7 +16,7 @@
bool bootconfig_supported;
};
-bool ParseCommandLineFlags(int* argc, char*** argv,
+bool ParseCommandLineFlags(std::vector<std::string>& flags,
KernelConfig* kernel_config);
// Must be called after ParseCommandLineFlags.
CuttlefishConfig InitializeCuttlefishConfiguration(
diff --git a/host/commands/bt_connector/OWNERS b/host/commands/bt_connector/OWNERS
index e8a4a00..e791d83 100644
--- a/host/commands/bt_connector/OWNERS
+++ b/host/commands/bt_connector/OWNERS
@@ -1,2 +1,3 @@
+include device/google/cuttlefish:/OWNERS
include platform/system/bt:/OWNERS
jeongik@google.com
\ No newline at end of file
diff --git a/host/commands/stop_cvd/Android.bp b/host/commands/cvd/Android.bp
similarity index 68%
copy from host/commands/stop_cvd/Android.bp
copy to host/commands/cvd/Android.bp
index a670a25..c8b9713 100644
--- a/host/commands/stop_cvd/Android.bp
+++ b/host/commands/cvd/Android.bp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2018 The Android Open Source Project
+// Copyright (C) 2021 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.
@@ -17,22 +17,30 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_binary {
- name: "stop_cvd",
+cc_binary_host {
+ name: "cvd",
srcs: [
"main.cc",
],
shared_libs: [
"libbase",
- "libcuttlefish_fs",
"libcuttlefish_utils",
- "libcuttlefish_allocd_utils",
"libjsoncpp",
],
static_libs: [
"libcuttlefish_host_config",
- "libcuttlefish_vm_manager",
- "libgflags",
],
- defaults: ["cuttlefish_host", "cuttlefish_libicuuc"],
+ required: [
+ "cvd_internal_host_bugreport",
+ "cvd_internal_start",
+ "cvd_internal_status",
+ "cvd_internal_stop",
+ ],
+ symlinks: [
+ "cvd_host_bugreport",
+ "cvd_status",
+ "launch_cvd",
+ "stop_cvd",
+ ],
+ defaults: ["cuttlefish_host"],
}
diff --git a/host/commands/cvd/main.cc b/host/commands/cvd/main.cc
new file mode 100644
index 0000000..a933616
--- /dev/null
+++ b/host/commands/cvd/main.cc
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include "common/libs/utils/files.h"
+#include "common/libs/utils/flag_parser.h"
+#include "common/libs/utils/subprocess.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+namespace cuttlefish {
+namespace {
+
+constexpr char kHostBugreportBin[] = "cvd_internal_host_bugreport";
+constexpr char kStartBin[] = "cvd_internal_start";
+constexpr char kStatusBin[] = "cvd_internal_status";
+constexpr char kStopBin[] = "cvd_internal_stop";
+
+constexpr char kHelpBin[] = "help_placeholder"; // Unused, prints kHelpMessage.
+constexpr char kHelpMessage[] = R"(Cuttlefish Virtual Device (CVD) CLI.
+
+usage: cvd <command>
+
+Commands:
+ help Print this message.
+ help <command> Print help for a command.
+ start Start a device.
+ stop Stop a running device.
+ status Check and print the state of a running instance.
+ host_bugreport Capture a host bugreport, including configs, logs, and tombstones.
+)";
+
+const std::map<std::string, std::string> CommandToBinaryMap = {
+ {"help", kHelpBin},
+ {"host_bugreport", kHostBugreportBin},
+ {"cvd_host_bugreport", kHostBugreportBin},
+ {"start", kStartBin},
+ {"launch_cvd", kStartBin},
+ {"status", kStatusBin},
+ {"cvd_status", kStatusBin},
+ {"stop", kStopBin},
+ {"stop_cvd", kStopBin}};
+
+int CvdMain(int argc, char** argv) {
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ std::vector<Flag> flags;
+
+ std::string bin;
+ std::string program_name = cpp_basename(argv[0]);
+ std::string subcommand_name = program_name;
+ if (program_name == "cvd") {
+ if (argc == 1) {
+ // Show help if user invokes `cvd` alone.
+ subcommand_name = "help";
+ } else {
+ subcommand_name = argv[1];
+ }
+ }
+ auto subcommand_bin = CommandToBinaryMap.find(subcommand_name);
+ if (subcommand_bin == CommandToBinaryMap.end()) {
+ // Show help if subcommand not found.
+ bin = kHelpBin;
+ } else {
+ bin = subcommand_bin->second;
+ }
+
+ // Allow `cvd --help` and `cvd help --help`.
+ if (bin == kHelpBin) {
+ flags.emplace_back(HelpFlag(flags, kHelpMessage));
+ }
+
+ // Collect args, skipping the program name.
+ size_t args_to_skip = program_name == "cvd" ? 2 : 1;
+ std::vector<std::string> args =
+ ArgsToVec(argc - args_to_skip, argv + args_to_skip);
+ CHECK(ParseFlags(flags, args));
+
+ if (bin == kHelpBin) {
+ // Handle `cvd help`
+ if (args.empty()) {
+ std::cout << kHelpMessage;
+ return 0;
+ }
+
+ // Handle `cvd help <subcommand>` by calling the subcommand with --help.
+ auto it = CommandToBinaryMap.find(args[0]);
+ if (it == CommandToBinaryMap.end() || args[0] == "help") {
+ std::cout << kHelpMessage;
+ return 0;
+ }
+ bin = it->second;
+ args = {"--help"};
+ }
+
+ setenv(
+ "ANDROID_SOONG_HOST_OUT",
+ android::base::Dirname(android::base::GetExecutableDirectory()).c_str(),
+ /*overwrite=*/true);
+ Command command(HostBinaryPath(bin));
+ for (const std::string& arg : args) {
+ command.AddParameter(arg);
+ }
+ return command.Start().Wait();
+}
+
+} // namespace
+} // namespace cuttlefish
+
+int main(int argc, char** argv) { return cuttlefish::CvdMain(argc, argv); }
diff --git a/host/commands/cvd_status/cvd_status.cc b/host/commands/cvd_status/cvd_status.cc
deleted file mode 100644
index 81436d4..0000000
--- a/host/commands/cvd_status/cvd_status.cc
+++ /dev/null
@@ -1,111 +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.
- */
-
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-
-#include <algorithm>
-#include <cstdlib>
-#include <fstream>
-#include <iomanip>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <gflags/gflags.h>
-#include <android-base/logging.h>
-
-#include "common/libs/fs/shared_fd.h"
-#include "common/libs/fs/shared_select.h"
-#include "common/libs/utils/environment.h"
-#include "host/commands/run_cvd/runner_defs.h"
-#include "host/libs/config/cuttlefish_config.h"
-#include "host/libs/vm_manager/vm_manager.h"
-
-DEFINE_int32(wait_for_launcher, 5,
- "How many seconds to wait for the launcher to respond to the status "
- "command. A value of zero means wait indefinetly");
-
-int main(int argc, char** argv) {
- ::android::base::InitLogging(argv, android::base::StderrLogger);
- google::ParseCommandLineFlags(&argc, &argv, true);
-
- auto config = cuttlefish::CuttlefishConfig::Get();
- if (!config) {
- LOG(ERROR) << "Failed to obtain config object";
- return 1;
- }
-
- auto instance = config->ForDefaultInstance();
- auto monitor_path = instance.launcher_monitor_socket_path();
- if (monitor_path.empty()) {
- LOG(ERROR) << "No path to launcher monitor found";
- return 2;
- }
- auto monitor_socket = cuttlefish::SharedFD::SocketLocalClient(
- monitor_path.c_str(), false, SOCK_STREAM, FLAGS_wait_for_launcher);
- if (!monitor_socket->IsOpen()) {
- LOG(ERROR) << "Unable to connect to launcher monitor at " << monitor_path
- << ": " << monitor_socket->StrError();
- return 3;
- }
- auto request = cuttlefish::LauncherAction::kStatus;
- auto bytes_sent = monitor_socket->Send(&request, sizeof(request), 0);
- if (bytes_sent < 0) {
- LOG(ERROR) << "Error sending launcher monitor the status command: "
- << monitor_socket->StrError();
- return 4;
- }
- // Perform a select with a timeout to guard against launcher hanging
- cuttlefish::SharedFDSet read_set;
- read_set.Set(monitor_socket);
- struct timeval timeout = {FLAGS_wait_for_launcher, 0};
- int selected = cuttlefish::Select(&read_set, nullptr, nullptr,
- FLAGS_wait_for_launcher <= 0 ? nullptr : &timeout);
- if (selected < 0){
- LOG(ERROR) << "Failed communication with the launcher monitor: "
- << strerror(errno);
- return 5;
- }
- if (selected == 0) {
- LOG(ERROR) << "Timeout expired waiting for launcher monitor to respond";
- return 6;
- }
- cuttlefish::LauncherResponse response;
- auto bytes_recv = monitor_socket->Recv(&response, sizeof(response), 0);
- if (bytes_recv < 0) {
- LOG(ERROR) << "Error receiving response from launcher monitor: "
- << monitor_socket->StrError();
- return 7;
- }
- if (response != cuttlefish::LauncherResponse::kSuccess) {
- LOG(ERROR) << "Received '" << static_cast<char>(response)
- << "' response from launcher monitor";
- return 8;
- }
- LOG(INFO) << "run_cvd is active.";
- return 0;
-}
diff --git a/host/commands/fetcher/build_api.cc b/host/commands/fetcher/build_api.cc
index 69229f0..16f95db 100644
--- a/host/commands/fetcher/build_api.cc
+++ b/host/commands/fetcher/build_api.cc
@@ -128,16 +128,27 @@
}
std::vector<Artifact> BuildApi::Artifacts(const DeviceBuild& build) {
- std::string url = BUILD_API + "/builds/" + build.id + "/" + build.target
- + "/attempts/latest/artifacts?maxResults=1000";
- auto artifacts_json = curl.DownloadToJson(url, Headers());
- CHECK(!artifacts_json.isMember("error")) << "Error fetching the artifacts of "
- << build << ". Response was " << artifacts_json;
-
+ std::string page_token = "";
std::vector<Artifact> artifacts;
- for (const auto& artifact_json : artifacts_json["artifacts"]) {
- artifacts.emplace_back(artifact_json);
- }
+ do {
+ std::string url = BUILD_API + "/builds/" + build.id + "/" + build.target +
+ "/attempts/latest/artifacts?maxResults=1000";
+ if (page_token != "") {
+ url += "&pageToken=" + page_token;
+ }
+ auto artifacts_json = curl.DownloadToJson(url, Headers());
+ CHECK(!artifacts_json.isMember("error"))
+ << "Error fetching the artifacts of " << build << ". Response was "
+ << artifacts_json;
+ if (artifacts_json.isMember("nextPageToken")) {
+ page_token = artifacts_json["nextPageToken"].asString();
+ } else {
+ page_token = "";
+ }
+ for (const auto& artifact_json : artifacts_json["artifacts"]) {
+ artifacts.emplace_back(artifact_json);
+ }
+ } while (page_token != "");
return artifacts;
}
@@ -164,22 +175,17 @@
bool BuildApi::ArtifactToFile(const DeviceBuild& build,
const std::string& artifact,
const std::string& path) {
- std::string url;
- if (credential_source) {
- url = BUILD_API + "/builds/" + build.id + "/" + build.target +
- "/attempts/latest/artifacts/" + artifact + "?alt=media";
- } else {
- std::string download_url_endpoint =
- BUILD_API + "/builds/" + build.id + "/" + build.target +
- "/attempts/latest/artifacts/" + artifact + "/url";
- auto download_url_json = curl.DownloadToJson(download_url_endpoint);
- if (!download_url_json.isMember("signedUrl")) {
- LOG(ERROR) << "URL endpoint did not have json path";
- return false;
- }
- url = download_url_json["signedUrl"].asString();
+ std::string download_url_endpoint =
+ BUILD_API + "/builds/" + build.id + "/" + build.target +
+ "/attempts/latest/artifacts/" + artifact + "/url";
+ auto download_url_json =
+ curl.DownloadToJson(download_url_endpoint, Headers());
+ if (!download_url_json.isMember("signedUrl")) {
+ LOG(ERROR) << "URL endpoint did not have json path: " << download_url_json;
+ return false;
}
- return curl.DownloadToFile(url, path, Headers());
+ std::string url = download_url_json["signedUrl"].asString();
+ return curl.DownloadToFile(url, path);
}
bool BuildApi::ArtifactToFile(const DirectoryBuild& build,
diff --git a/host/commands/cvd_status/Android.bp b/host/commands/health/Android.bp
similarity index 90%
rename from host/commands/cvd_status/Android.bp
rename to host/commands/health/Android.bp
index ae48d19..1fa59ab 100644
--- a/host/commands/cvd_status/Android.bp
+++ b/host/commands/health/Android.bp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2018 The Android Open Source Project
+// Copyright (C) 2021 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.
@@ -18,9 +18,9 @@
}
cc_binary {
- name: "cvd_status",
+ name: "health",
srcs: [
- "cvd_status.cc",
+ "health.cpp",
],
shared_libs: [
"libbase",
diff --git a/host/commands/health/health.cpp b/host/commands/health/health.cpp
new file mode 100644
index 0000000..37cce86
--- /dev/null
+++ b/host/commands/health/health.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <iostream>
+//
+#include <android-base/logging.h>
+#include <gflags/gflags.h>
+//
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/vm_manager/vm_manager.h"
+
+std::string GetControlSocketPath(const cuttlefish::CuttlefishConfig& config) {
+ return config.ForDefaultInstance().PerInstanceInternalPath(
+ "crosvm_control.sock");
+}
+
+std::string USAGE_MESSAGE =
+ "<key> [value]\n"
+ "Excluding the value will enumerate the possible values to set\n"
+ "\n"
+ "\"status [value]\" - battery status: "
+ "unknown/charging/discharging/notcharging/full\n"
+ "\"health [value]\" - battery health\n"
+ "\"present [value]\" - battery present: 1 or 0\n"
+ "\"capacity [value]\" - battery capacity: 0 to 100\n"
+ "\"aconline [value]\" - battery ac online: 1 or 0\n";
+
+int status() {
+ std::cout
+ << "health status [value]\n"
+ "\"value\" - unknown, charging, discharging, notcharging, full\n";
+ return 0;
+}
+
+int health() {
+ std::cout << "health health [value]\n"
+ "\"value\" - unknown, good, overheat, dead, overvoltage, "
+ "unexpectedfailure,\n"
+ " cold, watchdogtimerexpire, safetytimerexpire, "
+ "overcurrent\n";
+ return 0;
+}
+
+int present() {
+ std::cout << "health present [value]\n"
+ "\"value\" - 1, 0\n";
+ return 0;
+}
+
+int capacity() {
+ std::cout << "health capacity [value]\n"
+ "\"value\" - 0 to 100\n";
+ return 0;
+}
+
+int aconline() {
+ std::cout << "health aconline [value]\n"
+ "\"value\" - 1, 0\n";
+ return 0;
+}
+
+int usage() {
+ std::cout << "health " << USAGE_MESSAGE;
+ return 1;
+}
+
+int main(int argc, char** argv) {
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ gflags::SetUsageMessage(USAGE_MESSAGE);
+
+ auto config = cuttlefish::CuttlefishConfig::Get();
+ if (!config) {
+ LOG(ERROR) << "Failed to obtain config object";
+ return 1;
+ }
+
+ if (argc != 2 && argc != 3) {
+ return usage();
+ }
+
+ std::string key = argv[1];
+ std::string value = "";
+ if (argc == 3) {
+ value = argv[2];
+ }
+
+ if (argc == 2 || value == "--help" || value == "-h" || value == "help") {
+ if (key == "status") {
+ return status();
+ } else if (key == "health") {
+ return health();
+ } else if (key == "present") {
+ return present();
+ } else if (key == "capacity") {
+ return capacity();
+ } else if (key == "aconline") {
+ return aconline();
+ } else {
+ return usage();
+ }
+ }
+
+ cuttlefish::Command command(config->crosvm_binary());
+ command.AddParameter("battery");
+ command.AddParameter("goldfish");
+ command.AddParameter(key);
+ command.AddParameter(value);
+ command.AddParameter(GetControlSocketPath(*config));
+
+ std::string output, error;
+ auto ret = RunWithManagedStdio(std::move(command), NULL, &output, &error);
+ if (ret != 0) {
+ LOG(ERROR) << "goldfish battery returned: " << ret << "\n" << output << "\n" << error;
+ }
+ return ret;
+}
diff --git a/host/commands/cvd_host_bugreport/Android.bp b/host/commands/host_bugreport/Android.bp
similarity index 96%
rename from host/commands/cvd_host_bugreport/Android.bp
rename to host/commands/host_bugreport/Android.bp
index 4f6aeda..3428360 100644
--- a/host/commands/cvd_host_bugreport/Android.bp
+++ b/host/commands/host_bugreport/Android.bp
@@ -18,7 +18,7 @@
}
cc_binary {
- name: "cvd_host_bugreport",
+ name: "cvd_internal_host_bugreport",
srcs: [
"main.cc",
],
diff --git a/host/commands/cvd_host_bugreport/main.cc b/host/commands/host_bugreport/main.cc
similarity index 100%
rename from host/commands/cvd_host_bugreport/main.cc
rename to host/commands/host_bugreport/main.cc
diff --git a/host/commands/kernel_log_monitor/kernel_log_server.cc b/host/commands/kernel_log_monitor/kernel_log_server.cc
index 66fafab..3dbbe63 100644
--- a/host/commands/kernel_log_monitor/kernel_log_server.cc
+++ b/host/commands/kernel_log_monitor/kernel_log_server.cc
@@ -16,7 +16,8 @@
#include "host/commands/kernel_log_monitor/kernel_log_server.h"
-#include <map>
+#include <string>
+#include <tuple>
#include <utility>
#include <android-base/logging.h>
@@ -25,26 +26,42 @@
#include "common/libs/fs/shared_select.h"
#include "host/libs/config/cuttlefish_config.h"
-using cuttlefish::SharedFD;
-
namespace {
-static const std::map<std::string, std::string> kInformationalPatterns = {
+
+using cuttlefish::SharedFD;
+using monitor::Event;
+
+constexpr struct {
+ std::string_view match; // Substring to match in the kernel logs
+ std::string_view prefix; // Prefix value to output, describing the entry
+} kInformationalPatterns[] = {
{"U-Boot ", "GUEST_UBOOT_VERSION: "},
{"] Linux version ", "GUEST_KERNEL_VERSION: "},
{"GUEST_BUILD_FINGERPRINT: ", "GUEST_BUILD_FINGERPRINT: "},
};
-static const std::map<std::string, monitor::Event> kStageToEventMap = {
- {cuttlefish::kBootStartedMessage, monitor::Event::BootStarted},
- {cuttlefish::kBootCompletedMessage, monitor::Event::BootCompleted},
- {cuttlefish::kBootFailedMessage, monitor::Event::BootFailed},
- {cuttlefish::kMobileNetworkConnectedMessage,
- monitor::Event::MobileNetworkConnected},
- {cuttlefish::kWifiConnectedMessage, monitor::Event::WifiNetworkConnected},
- {cuttlefish::kEthernetConnectedMessage, monitor::Event::EthernetNetworkConnected},
+enum EventFormat {
+ kBare, // Just an event, no extra data
+ kKeyValuePair, // <stage> <key>=<value>
+};
+
+constexpr struct {
+ std::string_view stage; // substring in the log identifying the stage
+ Event event; // emitted when the stage is encountered
+ EventFormat format; // how the log message is formatted
+} kStageTable[] = {
+ {cuttlefish::kBootStartedMessage, Event::BootStarted, kBare},
+ {cuttlefish::kBootCompletedMessage, Event::BootCompleted, kBare},
+ {cuttlefish::kBootFailedMessage, Event::BootFailed, kKeyValuePair},
+ {cuttlefish::kMobileNetworkConnectedMessage, Event::MobileNetworkConnected,
+ kBare},
+ {cuttlefish::kWifiConnectedMessage, Event::WifiNetworkConnected, kBare},
+ {cuttlefish::kEthernetConnectedMessage, Event::EthernetNetworkConnected,
+ kBare},
// TODO(b/131864854): Replace this with a string less likely to change
- {"init: starting service 'adbd'...", monitor::Event::AdbdStarted},
- {cuttlefish::kScreenChangedMessage, monitor::Event::ScreenChanged},
+ {"init: starting service 'adbd'...", Event::AdbdStarted, kBare},
+ {cuttlefish::kScreenChangedMessage, Event::ScreenChanged, kKeyValuePair},
+ {cuttlefish::kKernelLoadedMessage, Event::KernelLoaded, kBare},
};
void ProcessSubscriptions(
@@ -109,17 +126,13 @@
// 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;
+ for (auto& [match, prefix] : kInformationalPatterns) {
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;
+ for (const auto& [stage, event, format] : kStageTable) {
auto pos = line_.find(stage);
if (std::string::npos != pos) {
// Log the stage
@@ -128,23 +141,26 @@
Json::Value message;
message["event"] = event;
Json::Value metadata;
- // Expect space-separated key=value pairs in the log message.
- const auto& fields = android::base::Split(
- line_.substr(pos + stage.size()), " ");
- for (std::string field : fields) {
- field = android::base::Trim(field);
- if (field.empty()) {
- // Expected; android::base::Split() always returns at least
- // one (possibly empty) string.
- LOG(DEBUG) << "Empty field for line: " << line_;
- continue;
+
+ if (format == kKeyValuePair) {
+ // Expect space-separated key=value pairs in the log message.
+ const auto& fields =
+ android::base::Split(line_.substr(pos + stage.size()), " ");
+ for (std::string field : fields) {
+ field = android::base::Trim(field);
+ if (field.empty()) {
+ // Expected; android::base::Split() always returns at least
+ // one (possibly empty) string.
+ LOG(DEBUG) << "Empty field for line: " << line_;
+ continue;
+ }
+ const auto& keyvalue = android::base::Split(field, "=");
+ if (keyvalue.size() != 2) {
+ LOG(WARNING) << "Field is not in key=value format: " << field;
+ continue;
+ }
+ metadata[keyvalue[0]] = keyvalue[1];
}
- const auto& keyvalue = android::base::Split(field, "=");
- if (keyvalue.size() != 2) {
- LOG(WARNING) << "Field is not in key=value format: " << field;
- continue;
- }
- metadata[keyvalue[0]] = keyvalue[1];
}
message["metadata"] = metadata;
ProcessSubscriptions(message, &subscribers_);
@@ -154,7 +170,7 @@
if (deprecated_boot_completed_) {
// Write to host kernel log
FILE* log = popen("/usr/bin/sudo /usr/bin/tee /dev/kmsg", "w");
- fprintf(log, "%s\n", stage.c_str());
+ fprintf(log, "%s\n", std::string(stage).c_str());
fclose(log);
}
}
diff --git a/host/commands/kernel_log_monitor/kernel_log_server.h b/host/commands/kernel_log_monitor/kernel_log_server.h
index 659a372..c3c6b3f 100644
--- a/host/commands/kernel_log_monitor/kernel_log_server.h
+++ b/host/commands/kernel_log_monitor/kernel_log_server.h
@@ -37,6 +37,9 @@
AdbdStarted = 5,
ScreenChanged = 6,
EthernetNetworkConnected = 7,
+ KernelLoaded = 8, // BootStarted actually comes quite late in the boot.
+ // KernelLoaded is the earliest possible indicator
+ // that we're booting a device.
};
enum class SubscriptionAction {
diff --git a/host/commands/mk_cdisk/Android.bp b/host/commands/mk_cdisk/Android.bp
index 266dd15..a0cf8ba 100644
--- a/host/commands/mk_cdisk/Android.bp
+++ b/host/commands/mk_cdisk/Android.bp
@@ -27,13 +27,14 @@
"libcuttlefish_utils",
"libbase",
"libjsoncpp",
- "libprotobuf-cpp-full",
+ "liblog",
"libz",
],
static_libs: [
"libcdisk_spec",
"libext2_uuid",
"libimage_aggregator",
+ "libprotobuf-cpp-lite",
"libsparse",
],
defaults: ["cuttlefish_host"],
diff --git a/host/commands/mk_cdisk/mk_cdisk.cc b/host/commands/mk_cdisk/mk_cdisk.cc
index a9949dc..028547c 100644
--- a/host/commands/mk_cdisk/mk_cdisk.cc
+++ b/host/commands/mk_cdisk/mk_cdisk.cc
@@ -50,7 +50,7 @@
// {
// "label": string,
// "path": string,
-// (opt) "read_only": bool
+// "writable": bool, // optional. defaults to false.
// }
// ]
// }
@@ -67,14 +67,14 @@
for (const Json::Value& part : root["partitions"]) {
const std::string label = part["label"].asString();
const std::string path = part["path"].asString();
- const bool read_only =
- part["read_only"].asBool(); // default: false (if null)
+ const bool writable =
+ part["writable"].asBool(); // default: false (if null)
if (!FileExists(path)) {
return Error() << "bad config: Can't find \'" << path << '\'';
}
partitions.push_back(
- ImagePartition{label, path, kLinuxFilesystem, read_only});
+ ImagePartition{label, path, kLinuxFilesystem, .read_only = !writable});
}
if (partitions.empty()) {
diff --git a/host/commands/modem_simulator/call_service.cpp b/host/commands/modem_simulator/call_service.cpp
index 678da4f..3dd6076 100644
--- a/host/commands/modem_simulator/call_service.cpp
+++ b/host/commands/modem_simulator/call_service.cpp
@@ -194,7 +194,7 @@
client.SendCommandResponse(kCmeErrorNoNetworkService);
return;
}
- auto local_host_port = GetHostPort();
+ auto local_host_port = GetHostId();
if (local_host_port == remote_port) {
client.SendCommandResponse(kCmeErrorOperationNotAllowed);
return;
@@ -249,11 +249,9 @@
CallStatus::CallState state) {
if (call.is_remote_call && call.remote_client != std::nullopt) {
std::stringstream ss;
- ss << "AT+REMOTECALL=" << state << ","
- << call.is_voice_mode << ","
- << call.is_multi_party << ",\""
- << GetHostPort() << "\","
- << call.is_international;
+ ss << "AT+REMOTECALL=" << state << "," << call.is_voice_mode << ","
+ << call.is_multi_party << ",\"" << GetHostId() << "\","
+ << call.is_international;
SendCommandToRemote(*(call.remote_client), ss.str());
if (state == CallStatus::CALL_STATE_HANGUP) {
diff --git a/host/commands/modem_simulator/cf_device_config.cpp b/host/commands/modem_simulator/cf_device_config.cpp
index 559e1f8..81c60e7 100644
--- a/host/commands/modem_simulator/cf_device_config.cpp
+++ b/host/commands/modem_simulator/cf_device_config.cpp
@@ -22,14 +22,13 @@
namespace cuttlefish {
namespace modem {
-int DeviceConfig::host_port() {
+int DeviceConfig::host_id() {
if (!cuttlefish::CuttlefishConfig::Get()) {
- return 6500;
+ return 1000;
}
auto config = cuttlefish::CuttlefishConfig::Get();
auto instance = config->ForDefaultInstance();
- auto host_port = instance.host_port();
- return host_port;
+ return instance.modem_simulator_host_id();
}
std::string DeviceConfig::PerInstancePath(const char* file_name) {
diff --git a/host/commands/modem_simulator/channel_monitor.cpp b/host/commands/modem_simulator/channel_monitor.cpp
index e598f2d..941a8b4 100644
--- a/host/commands/modem_simulator/channel_monitor.cpp
+++ b/host/commands/modem_simulator/channel_monitor.cpp
@@ -45,7 +45,7 @@
if (response.back() != '\r') {
response += '\r';
}
- LOG(VERBOSE) << " AT< " << response;
+ LOG(DEBUG) << " AT< " << response;
std::lock_guard<std::mutex> autolock(const_cast<Client*>(this)->write_mutex);
client_fd->Write(response.data(), response.size());
@@ -153,7 +153,7 @@
if (r_pos != std::string::npos) {
auto command = commands.substr(pos, r_pos - pos);
if (command.size() > 0) { // "\r\r" ?
- LOG(VERBOSE) << "AT> " << command;
+ LOG(DEBUG) << "AT> " << command;
modem_->DispatchCommand(client, command);
}
pos = r_pos + 1; // Skip '\r'
diff --git a/host/commands/modem_simulator/device_config.h b/host/commands/modem_simulator/device_config.h
index 05bca77..d13a38a 100644
--- a/host/commands/modem_simulator/device_config.h
+++ b/host/commands/modem_simulator/device_config.h
@@ -26,7 +26,7 @@
class DeviceConfig {
public:
- static int host_port();
+ static int host_id();
static std::string PerInstancePath(const char* file_name);
static std::string DefaultHostArtifactsPath(const std::string& file);
static std::string ril_address_and_prefix();
diff --git a/host/commands/modem_simulator/files/iccprofile_for_sim0.xml b/host/commands/modem_simulator/files/iccprofile_for_sim0.xml
index 42fadf7..655c37a 100755
--- a/host/commands/modem_simulator/files/iccprofile_for_sim0.xml
+++ b/host/commands/modem_simulator/files/iccprofile_for_sim0.xml
@@ -23,6 +23,7 @@
<EF name="EF_PBR" id="4F30" structure="linear fixed">
<SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,62198205422100400283024F308A01058B036F0606800200808800</SIMIO>
<SIMIO cmd="B2" p1="1" p2="4" p3="40" data="">144,0,A81EC0034F3A01C1034F3306C5034F0902C4034F1104C6034F2503C9034F3107A905CA034F5008AA0FC2034F4A09C7034F4B0AC8034F4C0BFFFFFFFFFFFFFFFF</SIMIO>
+ <SIMIO cmd="B2" p1="2" p2="4" p3="40" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
</EF>
<EF name="EF_GAS" id="4F4C" structure="linear fixed">
<SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A8205422100140A83024F4C8A01058B036F060E800200C8880158</SIMIO>
@@ -38,7 +39,7 @@
<SIMIO cmd="B2" p1="A" p2="4" p3="E" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
</EF>
<EF name="EF_ADN" id="4F3A" structure="linear fixed">
- <SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A82054221001CFA83024F3A8A01058B036F060E80021B58880108</SIMIO>
+ <SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A82054221001C1483024F3A8A01058B036F060E80020230880108</SIMIO>
<SIMIO cmd="B2" p1="1" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="2" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="3" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
@@ -56,7 +57,9 @@
<SIMIO cmd="B2" p1="F" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="10" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="11" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
- <!-- EF_ADN P2=18...250 -->
+ <SIMIO cmd="B2" p1="12" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
+ <SIMIO cmd="B2" p1="13" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
+ <SIMIO cmd="B2" p1="14" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
</EF>
<EF name="EF_IAP" id="4F33" structure="linear fixed">
<SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A820542210001FA83024F338A01058B036F060E800200FA880130</SIMIO>
diff --git a/host/commands/modem_simulator/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml b/host/commands/modem_simulator/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml
index 188aca2..b4363da 100755
--- a/host/commands/modem_simulator/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml
+++ b/host/commands/modem_simulator/files/iccprofile_for_sim0_for_CtsCarrierApiTestCases.xml
@@ -23,6 +23,7 @@
<EF name="EF_PBR" id="4F30" structure="linear fixed">
<SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,62198205422100400283024F308A01058B036F0606800200808800</SIMIO>
<SIMIO cmd="B2" p1="1" p2="4" p3="40" data="">144,0,A81EC0034F3A01C1034F3306C5034F0902C4034F1104C6034F2503C9034F3107A905CA034F5008AA0FC2034F4A09C7034F4B0AC8034F4C0BFFFFFFFFFFFFFFFF</SIMIO>
+ <SIMIO cmd="B2" p1="2" p2="4" p3="40" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
</EF>
<EF name="EF_GAS" id="4F4C" structure="linear fixed">
<SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A8205422100140A83024F4C8A01058B036F060E800200C8880158</SIMIO>
@@ -38,7 +39,7 @@
<SIMIO cmd="B2" p1="A" p2="4" p3="E" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
</EF>
<EF name="EF_ADN" id="4F3A" structure="linear fixed">
- <SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A82054221001CFA83024F3A8A01058B036F060E80021B58880108</SIMIO>
+ <SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A82054221001C1483024F3A8A01058B036F060E80020230880108</SIMIO>
<SIMIO cmd="B2" p1="1" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="2" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="3" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
@@ -56,7 +57,9 @@
<SIMIO cmd="B2" p1="F" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="10" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="11" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
- <!-- EF_ADN P2=18...250 -->
+ <SIMIO cmd="B2" p1="12" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
+ <SIMIO cmd="B2" p1="13" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
+ <SIMIO cmd="B2" p1="14" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
</EF>
<EF name="EF_IAP" id="4F33" structure="linear fixed">
<SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A820542210001FA83024F338A01058B036F060E800200FA880130</SIMIO>
@@ -124,11 +127,11 @@
<ADF name="PKCS15" aid="A000000063504B43532D3135">
<File id="4300">
<CGLA cmd="00a40004024300">76,62228202412183024300A503C001408A01058B066F0601010001800201DC810201EE88009000</CGLA>
- <CGLA cmd="00b00000004300">516,30088200300404024310301AA0120410A000000476416E64726F696443545340300404024311301AA0120410A000000476416E64726F696443545341300404024312301AA0120410A000000476416E64726F696443545342300404024313301AA0120410A000000476416E64726F696443545343300404024314301AA0120410A000000476416E64726F696443545344300404024315301AA0120410A000000476416E64726F696443545345300404024316301AA0120410A000000476416E64726F6964435453463004040243173010A0080406FFFFFFFFFFFF300404024318301AA0120410A000000476416E64726F696443545347300404024313301AA0129000</CGLA>
+ <CGLA cmd="00b0000000">516,30088200300404024310301AA0120410A000000476416E64726F696443545340300404024311301AA0120410A000000476416E64726F696443545341300404024312301AA0120410A000000476416E64726F696443545342300404024313301AA0120410A000000476416E64726F696443545343300404024314301AA0120410A000000476416E64726F696443545344300404024315301AA0120410A000000476416E64726F696443545345300404024316301AA0120410A000000476416E64726F6964435453463004040243173010A0080406FFFFFFFFFFFF300404024318301AA0120410A000000476416E64726F696443545347300404024313301AA0129000</CGLA>
</File>
<File id="4318">
<CGLA cmd="00a40004024318">76,62228202412183024318A503C001408A01058B066F0601010001800200188102002A88009000</CGLA>
- <CGLA cmd="00b00000004318">124,3016041461ED377E85D386A8DFEE6B864BD85B0BFAA5AF8130220420CE7B2B47AE2B7552C8F92CC29124279883041FB623A5F194A82C9BF15D492AA09000</CGLA>
+ <CGLA cmd="00b0000000">124,3016041461ED377E85D386A8DFEE6B864BD85B0BFAA5AF8130220420CE7B2B47AE2B7552C8F92CC29124279883041FB623A5F194A82C9BF15D492AA09000</CGLA>
</File>
</ADF>
diff --git a/host/commands/modem_simulator/main.cpp b/host/commands/modem_simulator/main.cpp
index 794ad77..8bfcd97 100644
--- a/host/commands/modem_simulator/main.cpp
+++ b/host/commands/modem_simulator/main.cpp
@@ -114,7 +114,7 @@
// remote call, remote sms from other cuttlefish instance
std::string monitor_socket_name = "modem_simulator";
std::stringstream ss;
- ss << instance.host_port();
+ ss << instance.modem_simulator_host_id();
monitor_socket_name.append(ss.str());
auto monitor_socket = cuttlefish::SharedFD::SocketLocalServer(
diff --git a/host/commands/modem_simulator/modem_service.cpp b/host/commands/modem_simulator/modem_service.cpp
index 062da6f..7996e31 100644
--- a/host/commands/modem_simulator/modem_service.cpp
+++ b/host/commands/modem_simulator/modem_service.cpp
@@ -137,11 +137,8 @@
}
}
-std::string ModemService::GetHostPort() {
- auto host_port = cuttlefish::modem::DeviceConfig::host_port();
- std::stringstream ss;
- ss << host_port;
- return ss.str();
+std::string ModemService::GetHostId() {
+ return std::to_string(cuttlefish::modem::DeviceConfig::host_id());
}
} // namespace cuttlefish
diff --git a/host/commands/modem_simulator/modem_service.h b/host/commands/modem_simulator/modem_service.h
index 0b75d7d..744f618 100644
--- a/host/commands/modem_simulator/modem_service.h
+++ b/host/commands/modem_simulator/modem_service.h
@@ -104,7 +104,7 @@
void SendCommandToRemote(cuttlefish::SharedFD remote_client,
std::string response);
void CloseRemoteConnection(cuttlefish::SharedFD remote_client);
- static std::string GetHostPort();
+ static std::string GetHostId();
int32_t service_id_;
const std::vector<CommandHandler> command_handlers_;
diff --git a/host/commands/modem_simulator/network_service.cpp b/host/commands/modem_simulator/network_service.cpp
index fddf5d4..56f14e5 100644
--- a/host/commands/modem_simulator/network_service.cpp
+++ b/host/commands/modem_simulator/network_service.cpp
@@ -57,6 +57,10 @@
[this](const Client& client, std::string& cmd) {
this->HandleRadioPower(client, cmd);
}),
+ CommandHandler("+REMOTECFUN=",
+ [this](const Client& client, std::string& cmd) {
+ this->HandleRadioPower(client, cmd);
+ }),
CommandHandler(
"+CSQ",
[this](const Client& client) { this->HandleSignalStrength(client); }),
diff --git a/host/commands/modem_simulator/sim_service.cpp b/host/commands/modem_simulator/sim_service.cpp
index a4e660d..e6eced5 100644
--- a/host/commands/modem_simulator/sim_service.cpp
+++ b/host/commands/modem_simulator/sim_service.cpp
@@ -911,10 +911,17 @@
return;
}
+ SimFileSystem::EFId fileid = (SimFileSystem::EFId)std::stoi(id, nullptr, 16);
if (path == "") {
- SimFileSystem::EFId fileid = (SimFileSystem::EFId)std::stoi(id, nullptr, 16);
path = SimFileSystem::GetUsimEFPath(fileid);
}
+ // EF_ADN under DF_PHONEBOOK is mapped to EF_ADN under DF_TELECOM per
+ // 3GPP TS 31.102 4.4.2
+ if (fileid == SimFileSystem::EF_ADN &&
+ path == SimFileSystem::GetUsimEFPath(fileid)) {
+ id = "4F3A";
+ path = MF_SIM + DF_TELECOM + DF_PHONEBOOK;
+ }
size_t pos = 0;
auto parent = root;
diff --git a/host/commands/modem_simulator/sms_service.cpp b/host/commands/modem_simulator/sms_service.cpp
index 0da21a6..6a61e7b 100644
--- a/host/commands/modem_simulator/sms_service.cpp
+++ b/host/commands/modem_simulator/sms_service.cpp
@@ -256,8 +256,8 @@
return;
}
- auto local_host_port = GetHostPort();
- auto pdu = sms_pdu.CreateRemotePDU(local_host_port);
+ auto local_host_id = GetHostId();
+ auto pdu = sms_pdu.CreateRemotePDU(local_host_id);
std::string command = "AT+REMOTESMS=" + pdu + "\r";
std::string token = "REM0";
@@ -292,10 +292,8 @@
return;
} else if (port >= kRemotePortRange.first &&
port <= kRemotePortRange.second) {
- std::stringstream ss;
- ss << port;
- auto remote_host_port = ss.str();
- if (GetHostPort() == remote_host_port) { // Send SMS to local host port
+ auto remote_host_port = std::to_string(port);
+ if (GetHostId() == remote_host_port) { // Send SMS to local host port
thread_looper_->PostWithDelay(
std::chrono::seconds(1),
makeSafeCallback<SmsService>(this, [&sms_pdu](SmsService* me) {
diff --git a/host/commands/modem_simulator/unittest/iccfile.txt b/host/commands/modem_simulator/unittest/iccfile.txt
index dec0da6..48e2a02 100755
--- a/host/commands/modem_simulator/unittest/iccfile.txt
+++ b/host/commands/modem_simulator/unittest/iccfile.txt
@@ -23,6 +23,7 @@
<EF name="EF_PBR" id="4F30" structure="linear fixed">
<SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,62198205422100400283024F308A01058B036F0606800200808800</SIMIO>
<SIMIO cmd="B2" p1="1" p2="4" p3="40" data="">144,0,A81EC0034F3A01C1034F3306C5034F0902C4034F1104C6034F2503C9034F3107A905CA034F5008AA0FC2034F4A09C7034F4B0AC8034F4C0BFFFFFFFFFFFFFFFF</SIMIO>
+ <SIMIO cmd="B2" p1="2" p2="4" p3="40" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
</EF>
<EF name="EF_GAS" id="4F4C" structure="linear fixed">
<SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A8205422100140A83024F4C8A01058B036F060E800200C8880158</SIMIO>
@@ -38,7 +39,7 @@
<SIMIO cmd="B2" p1="A" p2="4" p3="E" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
</EF>
<EF name="EF_ADN" id="4F3A" structure="linear fixed">
- <SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A82054221001CFA83024F3A8A01058B036F060E80021B58880108</SIMIO>
+ <SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A82054221001C1483024F3A8A01058B036F060E80020230880108</SIMIO>
<SIMIO cmd="B2" p1="1" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="2" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="3" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
@@ -56,7 +57,9 @@
<SIMIO cmd="B2" p1="F" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="10" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
<SIMIO cmd="B2" p1="11" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
- <!-- EF_ADN P2=18...250 -->
+ <SIMIO cmd="B2" p1="12" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
+ <SIMIO cmd="B2" p1="13" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
+ <SIMIO cmd="B2" p1="14" p2="4" p3="1C" data="">144,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</SIMIO>
</EF>
<EF name="EF_IAP" id="4F33" structure="linear fixed">
<SIMIO cmd="C0" p1="0" p2="0" p3="F" data="">144,0,621A820542210001FA83024F338A01058B036F060E800200FA880130</SIMIO>
diff --git a/host/commands/run_cvd/Android.bp b/host/commands/run_cvd/Android.bp
index 6100be5..16864b7 100644
--- a/host/commands/run_cvd/Android.bp
+++ b/host/commands/run_cvd/Android.bp
@@ -22,14 +22,21 @@
srcs: [
"boot_state_machine.cc",
"launch.cc",
+ "launch_adb.cpp",
+ "launch_modem.cpp",
+ "launch_streamer.cpp",
"main.cc",
+ "reporting.cpp",
"process_monitor.cc",
+ "server_loop.cpp",
+ "validate.cpp",
],
shared_libs: [
"libcuttlefish_fs",
"libcuttlefish_utils",
"libcuttlefish_kernel_log_monitor_utils",
"libbase",
+ "libfruit",
"libjsoncpp",
"libnl",
],
diff --git a/host/commands/run_cvd/boot_state_machine.cc b/host/commands/run_cvd/boot_state_machine.cc
index 64eb893..2d6fcc9 100644
--- a/host/commands/run_cvd/boot_state_machine.cc
+++ b/host/commands/run_cvd/boot_state_machine.cc
@@ -16,24 +16,181 @@
#include "host/commands/run_cvd/boot_state_machine.h"
+#include <gflags/gflags.h>
#include <memory>
#include <thread>
#include "android-base/logging.h"
#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/tee_logging.h"
#include "host/commands/kernel_log_monitor/kernel_log_server.h"
#include "host/commands/kernel_log_monitor/utils.h"
#include "host/commands/run_cvd/runner_defs.h"
+#include "host/libs/config/feature.h"
+
+DEFINE_int32(reboot_notification_fd, -1,
+ "A file descriptor to notify when boot completes.");
namespace cuttlefish {
+namespace {
-CvdBootStateMachine::CvdBootStateMachine(SharedFD fg_launcher_pipe,
- SharedFD reboot_notification,
- SharedFD boot_events_pipe)
- : fg_launcher_pipe_(fg_launcher_pipe),
- reboot_notification_(reboot_notification),
- state_(kBootStarted) {
- boot_event_handler_ = std::thread([this, boot_events_pipe]() {
+// Forks and returns the write end of a pipe to the child process. The parent
+// process waits for boot events to come through the pipe and exits accordingly.
+SharedFD DaemonizeLauncher(const CuttlefishConfig& config) {
+ auto instance = config.ForDefaultInstance();
+ SharedFD read_end, write_end;
+ if (!SharedFD::Pipe(&read_end, &write_end)) {
+ LOG(ERROR) << "Unable to create pipe";
+ return {}; // a closed FD
+ }
+ auto pid = fork();
+ if (pid) {
+ // Explicitly close here, otherwise we may end up reading forever if the
+ // child process dies.
+ write_end->Close();
+ RunnerExitCodes 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 = RunnerExitCodes::kPipeIOError;
+ } else if (exit_code == RunnerExitCodes::kSuccess) {
+ LOG(INFO) << "Virtual device booted successfully";
+ } else if (exit_code == RunnerExitCodes::kVirtualDeviceBootFailed) {
+ LOG(ERROR) << "Virtual device failed to boot";
+ } else {
+ LOG(ERROR) << "Unexpected exit code: " << exit_code;
+ }
+ if (exit_code == RunnerExitCodes::kSuccess) {
+ LOG(INFO) << kBootCompletedMessage;
+ } else {
+ LOG(INFO) << kBootFailedMessage;
+ }
+ std::exit(exit_code);
+ } else {
+ // The child returns the write end of the pipe
+ if (daemon(/*nochdir*/ 1, /*noclose*/ 1) != 0) {
+ LOG(ERROR) << "Failed to daemonize child process: " << strerror(errno);
+ std::exit(RunnerExitCodes::kDaemonizationError);
+ }
+ // Redirect standard I/O
+ auto log_path = instance.launcher_log_path();
+ auto log = SharedFD::Open(log_path.c_str(), O_CREAT | O_WRONLY | O_APPEND,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ if (!log->IsOpen()) {
+ LOG(ERROR) << "Failed to create launcher log file: " << log->StrError();
+ std::exit(RunnerExitCodes::kDaemonizationError);
+ }
+ ::android::base::SetLogger(
+ TeeLogger({{LogFileSeverity(), log, MetadataLevel::FULL}}));
+ auto dev_null = SharedFD::Open("/dev/null", O_RDONLY);
+ if (!dev_null->IsOpen()) {
+ LOG(ERROR) << "Failed to open /dev/null: " << dev_null->StrError();
+ std::exit(RunnerExitCodes::kDaemonizationError);
+ }
+ if (dev_null->UNMANAGED_Dup2(0) < 0) {
+ LOG(ERROR) << "Failed dup2 stdin: " << dev_null->StrError();
+ std::exit(RunnerExitCodes::kDaemonizationError);
+ }
+ if (log->UNMANAGED_Dup2(1) < 0) {
+ LOG(ERROR) << "Failed dup2 stdout: " << log->StrError();
+ std::exit(RunnerExitCodes::kDaemonizationError);
+ }
+ if (log->UNMANAGED_Dup2(2) < 0) {
+ LOG(ERROR) << "Failed dup2 seterr: " << log->StrError();
+ std::exit(RunnerExitCodes::kDaemonizationError);
+ }
+
+ read_end->Close();
+ return write_end;
+ }
+}
+
+class ProcessLeader : public Feature {
+ public:
+ INJECT(ProcessLeader(const CuttlefishConfig& config)) : config_(config) {}
+
+ SharedFD ForegroundLauncherPipe() { return foreground_launcher_pipe_; }
+
+ bool Enabled() const override { return true; }
+ std::string Name() const override { return "ProcessLeader"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ /* These two paths result in pretty different process state, but both
+ * achieve the same goal of making the current process the leader of a
+ * process group, and are therefore grouped together. */
+ if (config_.run_as_daemon()) {
+ foreground_launcher_pipe_ = DaemonizeLauncher(config_);
+ if (!foreground_launcher_pipe_->IsOpen()) {
+ return false;
+ }
+ } else {
+ // Make sure the launcher runs in its own process group even when running
+ // in the foreground
+ if (getsid(0) != getpid()) {
+ int retval = setpgid(0, 0);
+ if (retval) {
+ PLOG(ERROR) << "Failed to create new process group: ";
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig& config_;
+ SharedFD foreground_launcher_pipe_;
+};
+
+// 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 Feature {
+ public:
+ INJECT(CvdBootStateMachine(ProcessLeader& process_leader,
+ KernelLogPipeProvider& kernel_log_pipe_provider))
+ : process_leader_(process_leader),
+ kernel_log_pipe_provider_(kernel_log_pipe_provider),
+ state_(kBootStarted) {}
+
+ ~CvdBootStateMachine() { boot_event_handler_.join(); }
+
+ // Feature
+ bool Enabled() const override { return true; }
+ std::string Name() const override { return "CvdBootStateMachine"; }
+ std::unordered_set<Feature*> Dependencies() const {
+ return {
+ static_cast<Feature*>(&process_leader_),
+ static_cast<Feature*>(&kernel_log_pipe_provider_),
+ };
+ }
+
+ protected:
+ bool Setup() override {
+ fg_launcher_pipe_ = process_leader_.ForegroundLauncherPipe();
+ if (FLAGS_reboot_notification_fd >= 0) {
+ reboot_notification_ = SharedFD::Dup(FLAGS_reboot_notification_fd);
+ if (!reboot_notification_->IsOpen()) {
+ LOG(ERROR) << "Could not dup fd given for reboot_notification_fd";
+ return false;
+ }
+ close(FLAGS_reboot_notification_fd);
+ }
+ SharedFD boot_events_pipe = kernel_log_pipe_provider_.KernelLogPipe();
+ if (!boot_events_pipe->IsOpen()) {
+ LOG(ERROR) << "Could not get boot events pipe";
+ return false;
+ }
+ boot_event_handler_ = std::thread(
+ [this, boot_events_pipe]() { ThreadLoop(boot_events_pipe); });
+ return true;
+ }
+
+ private:
+ void ThreadLoop(SharedFD boot_events_pipe) {
while (true) {
SharedFDSet fd_set;
fd_set.Set(boot_events_pipe);
@@ -50,61 +207,72 @@
break;
}
}
- });
-}
+ }
-CvdBootStateMachine::~CvdBootStateMachine() { boot_event_handler_.join(); }
+ // Returns true if the machine is left in a final state
+ bool OnBootEvtReceived(SharedFD boot_events_pipe) {
+ std::optional<monitor::ReadEventResult> read_result =
+ monitor::ReadEvent(boot_events_pipe);
+ if (!read_result) {
+ LOG(ERROR) << "Failed to read a complete kernel log boot event.";
+ state_ |= kGuestBootFailed;
+ return MaybeWriteNotification();
+ }
-// Returns true if the machine is left in a final state
-bool CvdBootStateMachine::OnBootEvtReceived(SharedFD boot_events_pipe) {
- std::optional<monitor::ReadEventResult> read_result =
- monitor::ReadEvent(boot_events_pipe);
- if (!read_result) {
- LOG(ERROR) << "Failed to read a complete kernel log boot event.";
- state_ |= kGuestBootFailed;
+ if (read_result->event == monitor::Event::BootCompleted) {
+ LOG(INFO) << "Virtual device booted successfully";
+ state_ |= kGuestBootCompleted;
+ } else if (read_result->event == monitor::Event::BootFailed) {
+ LOG(ERROR) << "Virtual device failed to boot";
+ state_ |= kGuestBootFailed;
+ } // Ignore the other signals
+
return MaybeWriteNotification();
}
+ bool BootCompleted() const { return state_ & kGuestBootCompleted; }
+ bool BootFailed() const { return state_ & kGuestBootFailed; }
- if (read_result->event == monitor::Event::BootCompleted) {
- LOG(INFO) << "Virtual device booted successfully";
- state_ |= kGuestBootCompleted;
- } else if (read_result->event == monitor::Event::BootFailed) {
- LOG(ERROR) << "Virtual device failed to boot";
- state_ |= kGuestBootFailed;
- } // Ignore the other signals
-
- return MaybeWriteNotification();
-}
-
-bool CvdBootStateMachine::BootCompleted() const {
- return state_ & kGuestBootCompleted;
-}
-
-bool CvdBootStateMachine::BootFailed() const {
- return state_ & kGuestBootFailed;
-}
-
-void CvdBootStateMachine::SendExitCode(RunnerExitCodes exit_code, SharedFD fd) {
- fd->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
- fd->Close();
-}
-
-bool CvdBootStateMachine::MaybeWriteNotification() {
- std::vector<SharedFD> fds = {reboot_notification_, fg_launcher_pipe_};
- for (auto& fd : fds) {
- if (fd->IsOpen()) {
- if (BootCompleted()) {
- SendExitCode(RunnerExitCodes::kSuccess, fd);
- } else if (state_ & kGuestBootFailed) {
- SendExitCode(RunnerExitCodes::kVirtualDeviceBootFailed, fd);
+ void SendExitCode(RunnerExitCodes exit_code, SharedFD fd) {
+ fd->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
+ fd->Close();
+ }
+ bool MaybeWriteNotification() {
+ std::vector<SharedFD> fds = {reboot_notification_, fg_launcher_pipe_};
+ for (auto& fd : fds) {
+ if (fd->IsOpen()) {
+ if (BootCompleted()) {
+ SendExitCode(RunnerExitCodes::kSuccess, fd);
+ } else if (state_ & kGuestBootFailed) {
+ SendExitCode(RunnerExitCodes::kVirtualDeviceBootFailed, fd);
+ }
}
}
+ // Either we sent the code before or just sent it, in any case the state is
+ // final
+ return BootCompleted() || (state_ & kGuestBootFailed);
}
- // Either we sent the code before or just sent it, in any case the state is
- // final
- return BootCompleted() || (state_ & kGuestBootFailed);
+
+ ProcessLeader& process_leader_;
+ KernelLogPipeProvider& kernel_log_pipe_provider_;
+
+ std::thread boot_event_handler_;
+ SharedFD fg_launcher_pipe_;
+ SharedFD reboot_notification_;
+ int state_;
+ static const int kBootStarted = 0;
+ static const int kGuestBootCompleted = 1 << 0;
+ static const int kGuestBootFailed = 1 << 1;
+};
+
+} // namespace
+
+fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider>>
+bootStateMachineComponent() {
+ return fruit::createComponent()
+ .addMultibinding<Feature, ProcessLeader>()
+ .addMultibinding<Feature, CvdBootStateMachine>();
}
} // namespace cuttlefish
diff --git a/host/commands/run_cvd/boot_state_machine.h b/host/commands/run_cvd/boot_state_machine.h
index 0d02e72..796bb6f 100644
--- a/host/commands/run_cvd/boot_state_machine.h
+++ b/host/commands/run_cvd/boot_state_machine.h
@@ -15,39 +15,13 @@
*/
#pragma once
-#include <memory>
-#include <thread>
-
-#include "common/libs/fs/shared_fd.h"
-#include "host/commands/run_cvd/runner_defs.h"
+#include "host/commands/run_cvd/launch.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/feature.h"
namespace cuttlefish {
-// 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(SharedFD fg_launcher_pipe, SharedFD reboot_notification,
- SharedFD boot_events_pipe);
- ~CvdBootStateMachine();
-
- private:
- // Returns true if the machine is left in a final state
- bool OnBootEvtReceived(SharedFD boot_events_pipe);
- bool BootCompleted() const;
- bool BootFailed() const;
-
- void SendExitCode(RunnerExitCodes exit_code, SharedFD fd);
- bool MaybeWriteNotification();
-
- std::thread boot_event_handler_;
- SharedFD fg_launcher_pipe_;
- SharedFD reboot_notification_;
- int state_;
- static const int kBootStarted = 0;
- static const int kGuestBootCompleted = 1 << 0;
- static const int kGuestBootFailed = 1 << 1;
-};
+fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider>>
+bootStateMachineComponent();
} // namespace cuttlefish
diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc
index 3037e6b..78166e4 100644
--- a/host/commands/run_cvd/launch.cc
+++ b/host/commands/run_cvd/launch.cc
@@ -1,748 +1,667 @@
+//
+// 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/commands/run_cvd/launch.h"
-#include <sys/stat.h>
-#include <sys/types.h>
-
#include <android-base/logging.h>
-#include <android-base/strings.h>
+#include <unordered_set>
+#include <utility>
+#include <vector>
-#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/files.h"
-#include "common/libs/utils/size_utils.h"
+#include "common/libs/utils/subprocess.h"
+#include "host/commands/run_cvd/process_monitor.h"
+#include "host/commands/run_cvd/reporting.h"
#include "host/commands/run_cvd/runner_defs.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/inject.h"
#include "host/libs/config/known_paths.h"
-#include "host/libs/vm_manager/crosvm_manager.h"
-#include "host/libs/vm_manager/qemu_manager.h"
namespace cuttlefish {
-using vm_manager::QemuManager;
-
namespace {
-std::string GetAdbConnectorTcpArg(const CuttlefishConfig& config) {
- auto instance = config.ForDefaultInstance();
- return std::string{"0.0.0.0:"} + std::to_string(instance.host_port());
-}
-
-std::string GetAdbConnectorVsockArg(const CuttlefishConfig& config) {
- auto instance = config.ForDefaultInstance();
- return std::string{"vsock:"} + std::to_string(instance.vsock_guest_cid()) +
- std::string{":5555"};
-}
-
-bool AdbModeEnabled(const CuttlefishConfig& config, AdbMode mode) {
- return config.adb_mode().count(mode) > 0;
-}
-
-bool AdbVsockTunnelEnabled(const CuttlefishConfig& config) {
- auto instance = config.ForDefaultInstance();
- return instance.vsock_guest_cid() > 2 &&
- AdbModeEnabled(config, AdbMode::VsockTunnel);
-}
-
-bool AdbVsockHalfTunnelEnabled(const CuttlefishConfig& config) {
- auto instance = config.ForDefaultInstance();
- return instance.vsock_guest_cid() > 2 &&
- AdbModeEnabled(config, AdbMode::VsockHalfTunnel);
-}
-
-bool AdbTcpConnectorEnabled(const CuttlefishConfig& config) {
- bool vsock_tunnel = AdbVsockTunnelEnabled(config);
- bool vsock_half_tunnel = AdbVsockHalfTunnelEnabled(config);
- return config.run_adb_connector() && (vsock_tunnel || vsock_half_tunnel);
-}
-
-bool AdbVsockConnectorEnabled(const CuttlefishConfig& config) {
- return config.run_adb_connector() &&
- AdbModeEnabled(config, AdbMode::NativeVsock);
-}
-
-SharedFD CreateUnixInputServer(const std::string& path) {
- auto server =
- SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
- if (!server->IsOpen()) {
- LOG(ERROR) << "Unable to create unix input server: " << server->StrError();
- return {};
- }
- return server;
-}
-
-// Creates the frame and input sockets and add the relevant arguments to the vnc
-// server and webrtc commands
-void CreateStreamerServers(Command* cmd, const CuttlefishConfig& config) {
- SharedFD touch_server;
- SharedFD keyboard_server;
-
- auto instance = config.ForDefaultInstance();
- if (config.vm_manager() == QemuManager::name()) {
- cmd->AddParameter("-write_virtio_input");
-
- touch_server = SharedFD::VsockServer(instance.touch_server_port(),
- SOCK_STREAM);
- keyboard_server = SharedFD::VsockServer(instance.keyboard_server_port(),
- SOCK_STREAM);
- } else {
- touch_server = CreateUnixInputServer(instance.touch_socket_path());
- keyboard_server = CreateUnixInputServer(instance.keyboard_socket_path());
- }
- if (!touch_server->IsOpen()) {
- LOG(ERROR) << "Could not open touch server: " << touch_server->StrError();
- return;
- }
- cmd->AddParameter("-touch_fd=", touch_server);
-
- if (!keyboard_server->IsOpen()) {
- LOG(ERROR) << "Could not open keyboard server: "
- << keyboard_server->StrError();
- return;
- }
- cmd->AddParameter("-keyboard_fd=", keyboard_server);
-
- if (config.enable_webrtc() &&
- config.vm_manager() == vm_manager::CrosvmManager::name()) {
- SharedFD switches_server =
- CreateUnixInputServer(instance.switches_socket_path());
- if (!switches_server->IsOpen()) {
- LOG(ERROR) << "Could not open switches server: "
- << switches_server->StrError();
- return;
- }
- cmd->AddParameter("-switches_fd=", switches_server);
- }
-
- SharedFD frames_server = CreateUnixInputServer(instance.frames_socket_path());
- if (!frames_server->IsOpen()) {
- LOG(ERROR) << "Could not open frames server: " << frames_server->StrError();
- return;
- }
- cmd->AddParameter("-frame_server_fd=", frames_server);
-
- if (config.enable_audio()) {
- auto path = config.ForDefaultInstance().audio_server_path();
- auto audio_server =
- SharedFD::SocketLocalServer(path.c_str(), false, SOCK_SEQPACKET, 0666);
- if (!audio_server->IsOpen()) {
- LOG(ERROR) << "Could not create audio server: " << audio_server->StrError();
- return;
- }
- cmd->AddParameter("--audio_server_fd=", audio_server);
- }
+template <typename T>
+std::vector<T> single_element_emplace(T&& element) {
+ std::vector<T> vec;
+ vec.emplace_back(std::move(element));
+ return vec;
}
} // namespace
-std::vector<SharedFD> LaunchKernelLogMonitor(
- const CuttlefishConfig& config, ProcessMonitor* process_monitor,
- unsigned int number_of_event_pipes) {
- auto instance = config.ForDefaultInstance();
- auto log_name = instance.kernel_log_pipe_name();
- if (mkfifo(log_name.c_str(), 0600) != 0) {
- LOG(ERROR) << "Unable to create named pipe at " << log_name << ": "
- << strerror(errno);
- return {};
+CommandSource::~CommandSource() = default;
+
+KernelLogPipeProvider::~KernelLogPipeProvider() = default;
+
+class KernelLogMonitor : public CommandSource,
+ public KernelLogPipeProvider,
+ public DiagnosticInformation {
+ public:
+ INJECT(KernelLogMonitor(const CuttlefishConfig::InstanceSpecific& instance))
+ : instance_(instance) {}
+
+ // DiagnosticInformation
+ std::vector<std::string> Diagnostics() const override {
+ return {"Kernel log: " + instance_.PerInstancePath("kernel.log")};
}
- SharedFD pipe;
- // Open the pipe here (from the launcher) to ensure the pipe is not deleted
- // due to the usage counters in the kernel reaching zero. If this is not done
- // and the kernel_log_monitor crashes for some reason the VMM may get SIGPIPE.
- pipe = SharedFD::Open(log_name.c_str(), O_RDWR);
- Command command(KernelLogMonitorBinary());
- command.AddParameter("-log_pipe_fd=", pipe);
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command command(KernelLogMonitorBinary());
+ command.AddParameter("-log_pipe_fd=", fifo_);
- std::vector<SharedFD> ret;
-
- if (number_of_event_pipes > 0) {
- command.AddParameter("-subscriber_fds=");
- for (unsigned int i = 0; i < number_of_event_pipes; ++i) {
- SharedFD event_pipe_write_end, event_pipe_read_end;
- if (!SharedFD::Pipe(&event_pipe_read_end, &event_pipe_write_end)) {
- LOG(ERROR) << "Unable to create kernel log events pipe: " << strerror(errno);
- std::exit(RunnerExitCodes::kPipeIOError);
- }
- if (i > 0) {
- command.AppendToLastParameter(",");
- }
- command.AppendToLastParameter(event_pipe_write_end);
- ret.push_back(event_pipe_read_end);
- }
- }
-
- process_monitor->AddCommand(std::move(command));
-
- return ret;
-}
-
-void LaunchRootCanal(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor) {
- if (!config.enable_host_bluetooth()) {
- return;
- }
-
- auto instance = config.ForDefaultInstance();
- Command command(RootCanalBinary());
-
- // Test port
- command.AddParameter(instance.rootcanal_test_port());
- // HCI server port
- command.AddParameter(instance.rootcanal_hci_port());
- // Link server port
- command.AddParameter(instance.rootcanal_link_port());
- // Bluetooth controller properties file
- command.AddParameter("--controller_properties_file=",
- instance.rootcanal_config_file());
- // Default commands file
- command.AddParameter("--default_commands_file=",
- instance.rootcanal_default_commands_file());
-
- process_monitor->AddCommand(std::move(command));
- return;
-}
-
-void LaunchLogcatReceiver(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor) {
- auto instance = config.ForDefaultInstance();
- auto log_name = instance.logcat_pipe_name();
- if (mkfifo(log_name.c_str(), 0600) != 0) {
- LOG(ERROR) << "Unable to create named pipe at " << log_name << ": "
- << strerror(errno);
- return;
- }
-
- SharedFD pipe;
- // Open the pipe here (from the launcher) to ensure the pipe is not deleted
- // due to the usage counters in the kernel reaching zero. If this is not done
- // and the logcat_receiver crashes for some reason the VMM may get SIGPIPE.
- pipe = SharedFD::Open(log_name.c_str(), O_RDWR);
- Command command(LogcatReceiverBinary());
- command.AddParameter("-log_pipe_fd=", pipe);
-
- process_monitor->AddCommand(std::move(command));
- return;
-}
-
-void LaunchConfigServer(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor) {
- auto instance = config.ForDefaultInstance();
- auto port = instance.config_server_port();
- auto socket = SharedFD::VsockServer(port, SOCK_STREAM);
- if (!socket->IsOpen()) {
- LOG(ERROR) << "Unable to create configuration server socket: "
- << socket->StrError();
- std::exit(RunnerExitCodes::kConfigServerError);
- }
- Command cmd(ConfigServerBinary());
- cmd.AddParameter("-server_fd=", socket);
- process_monitor->AddCommand(std::move(cmd));
- return;
-}
-
-void LaunchTombstoneReceiver(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor) {
- auto instance = config.ForDefaultInstance();
-
- std::string tombstoneDir = instance.PerInstancePath("tombstones");
- if (!DirectoryExists(tombstoneDir.c_str())) {
- LOG(DEBUG) << "Setting up " << tombstoneDir;
- if (mkdir(tombstoneDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) <
- 0) {
- LOG(ERROR) << "Failed to create tombstone directory: " << tombstoneDir
- << ". Error: " << errno;
- exit(RunnerExitCodes::kTombstoneDirCreationError);
- return;
- }
- }
-
- auto port = instance.tombstone_receiver_port();
- auto socket = SharedFD::VsockServer(port, SOCK_STREAM);
- if (!socket->IsOpen()) {
- LOG(ERROR) << "Unable to create tombstone server socket: "
- << socket->StrError();
- std::exit(RunnerExitCodes::kTombstoneServerError);
- return;
- }
- Command cmd(TombstoneReceiverBinary());
- cmd.AddParameter("-server_fd=", socket);
- cmd.AddParameter("-tombstone_dir=", tombstoneDir);
-
- process_monitor->AddCommand(std::move(cmd));
- return;
-}
-
-void LaunchVNCServer(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor) {
- auto instance = config.ForDefaultInstance();
- // Launch the vnc server, don't wait for it to complete
- auto port_options = "-port=" + std::to_string(instance.vnc_server_port());
- Command vnc_server(VncServerBinary());
- vnc_server.AddParameter(port_options);
-
- CreateStreamerServers(&vnc_server, config);
-
- process_monitor->AddCommand(std::move(vnc_server));
-}
-
-void LaunchAdbConnectorIfEnabled(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config) {
- Command adb_connector(AdbConnectorBinary());
- std::set<std::string> addresses;
-
- if (AdbTcpConnectorEnabled(config)) {
- addresses.insert(GetAdbConnectorTcpArg(config));
- }
- if (AdbVsockConnectorEnabled(config)) {
- addresses.insert(GetAdbConnectorVsockArg(config));
- }
-
- if (addresses.size() > 0) {
- std::string address_arg = "--addresses=";
- for (auto& arg : addresses) {
- address_arg += arg + ",";
- }
- address_arg.pop_back();
- adb_connector.AddParameter(address_arg);
- process_monitor->AddCommand(std::move(adb_connector));
- }
-}
-
-void LaunchWebRTC(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config,
- SharedFD kernel_log_events_pipe) {
- if (config.ForDefaultInstance().start_webrtc_sig_server()) {
- Command sig_server(WebRtcSigServerBinary());
- sig_server.AddParameter("-assets_dir=", config.webrtc_assets_dir());
- if (!config.webrtc_certs_dir().empty()) {
- sig_server.AddParameter("-certs_dir=", config.webrtc_certs_dir());
- }
- sig_server.AddParameter("-http_server_port=", config.sig_server_port());
- process_monitor->AddCommand(std::move(sig_server));
- }
-
- // Currently there is no way to ensure the signaling server will already have
- // bound the socket to the port by the time the webrtc process runs (the
- // common technique of doing it from the launcher is not possible here as the
- // server library being used creates its own sockets). However, this issue is
- // mitigated slightly by doing some retrying and backoff in the webrtc process
- // when connecting to the websocket, so it shouldn't be an issue most of the
- // time.
- cuttlefish::SharedFD client_socket;
- cuttlefish::SharedFD host_socket;
- CHECK(cuttlefish::SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0,
- &client_socket, &host_socket))
- << "Could not open command socket for webRTC";
-
- auto stopper = [host_socket = std::move(host_socket)](cuttlefish::Subprocess* proc) {
- struct timeval timeout;
- timeout.tv_sec = 3;
- timeout.tv_usec = 0;
- CHECK(host_socket->SetSockOpt(SOL_SOCKET, SO_RCVTIMEO, &timeout,
- sizeof(timeout)) == 0)
- << "Could not set receive timeout";
-
- cuttlefish::WriteAll(host_socket, "C");
- char response[1];
- int read_ret = host_socket->Read(response, sizeof(response));
- if (read_ret != 0) {
- LOG(ERROR) << "Failed to read response from webrtc";
- }
- cuttlefish::KillSubprocess(proc);
- return true;
- };
-
- cuttlefish::Command webrtc(cuttlefish::WebRtcBinary(),
- cuttlefish::SubprocessStopper(stopper));
-
- webrtc.UnsetFromEnvironment({"http_proxy"});
-
- CreateStreamerServers(&webrtc, config);
-
- webrtc.AddParameter("--command_fd=", client_socket);
-
- webrtc.AddParameter("-kernel_log_events_fd=", kernel_log_events_pipe);
-
- LaunchCustomActionServers(webrtc, process_monitor, config);
-
- // TODO get from launcher params
- process_monitor->AddCommand(std::move(webrtc));
-}
-
-bool StopModemSimulator() {
- auto config = CuttlefishConfig::Get();
- auto instance = config->ForDefaultInstance();
-
- std::string monitor_socket_name = "modem_simulator";
- std::stringstream ss;
- ss << instance.host_port();
- monitor_socket_name.append(ss.str());
- auto monitor_sock = SharedFD::SocketLocalClient(
- monitor_socket_name.c_str(), true, SOCK_STREAM);
- if (!monitor_sock->IsOpen()) {
- LOG(ERROR) << "The connection to modem simulator is closed";
- return false;
- }
- std::string msg("STOP");
- if (monitor_sock->Write(msg.data(), msg.size()) < 0) {
- monitor_sock->Close();
- LOG(ERROR) << "Failed to send 'STOP' to modem simulator";
- return false;
- }
- char buf[64] = {0};
- if (monitor_sock->Read(buf, sizeof(buf)) <= 0) {
- monitor_sock->Close();
- LOG(ERROR) << "Failed to read message from modem simulator";
- return false;
- }
- if (strcmp(buf, "OK")) {
- monitor_sock->Close();
- LOG(ERROR) << "Read '" << buf << "' instead of 'OK' from modem simulator";
- return false;
- }
-
- return true;
-}
-
-void LaunchModemSimulatorIfEnabled(
- const CuttlefishConfig& config,
- ProcessMonitor* process_monitor) {
- if (!config.enable_modem_simulator()) {
- LOG(DEBUG) << "Modem simulator not enabled";
- return;
- }
-
- int instance_number = config.modem_simulator_instance_number();
- if (instance_number > 3 /* max value */ || instance_number < 0) {
- LOG(ERROR)
- << "Modem simulator instance number should range between 1 and 3";
- return;
- }
-
- Command cmd(
- ModemSimulatorBinary(), [](Subprocess* proc) {
- auto stopped = StopModemSimulator();
- if (stopped) {
- return true;
+ if (!event_pipe_write_ends_.empty()) {
+ command.AddParameter("-subscriber_fds=");
+ for (size_t i = 0; i < event_pipe_write_ends_.size(); i++) {
+ if (i > 0) {
+ command.AppendToLastParameter(",");
}
- LOG(WARNING) << "Failed to stop modem simulator nicely, "
- << "attempting to KILL";
- return KillSubprocess(proc);
- });
-
- auto sim_type = config.modem_simulator_sim_type();
- cmd.AddParameter(std::string{"-sim_type="} + std::to_string(sim_type));
-
- auto instance = config.ForDefaultInstance();
- auto ports = instance.modem_simulator_ports();
- cmd.AddParameter("-server_fds=");
- for (int i = 0; i < instance_number; ++i) {
- auto pos = ports.find(',');
- auto temp = (pos != std::string::npos) ? ports.substr(0, pos - 1) : ports;
- auto port = std::stoi(temp);
- ports = ports.substr(pos + 1);
-
- auto socket = SharedFD::VsockServer(port, SOCK_STREAM);
- if (!socket->IsOpen()) {
- LOG(ERROR) << "Unable to create modem simulator server socket: "
- << socket->StrError();
- std::exit(RunnerExitCodes::kModemSimulatorServerError);
- }
- if (i > 0) {
- cmd.AppendToLastParameter(",");
- }
- cmd.AppendToLastParameter(socket);
- }
-
- process_monitor->AddCommand(std::move(cmd));
-}
-
-void LaunchSocketVsockProxyIfEnabled(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config,
- SharedFD adbd_events_pipe) {
- auto instance = config.ForDefaultInstance();
- auto append = [](const std::string& s, const int i) -> std::string {
- return s + std::to_string(i);
- };
- if (AdbVsockTunnelEnabled(config)) {
- Command adb_tunnel(SocketVsockProxyBinary());
- adb_tunnel.AddParameter("-adbd_events_fd=", adbd_events_pipe);
- /**
- * This socket_vsock_proxy (a.k.a. sv proxy) runs on the host. It assumes that
- * another sv proxy runs inside the guest. see: shared/config/init.vendor.rc
- * The sv proxy in the guest exposes vsock:cid:6520 across the cuttlefish instances
- * in multi-tenancy. cid is different per instance.
- *
- * This host sv proxy should cooperate with the guest sv proxy. Thus, one end of
- * the tunnel is vsock:cid:6520 regardless of instance number. Another end faces
- * the host adb daemon via tcp. Thus, the server type is tcp here. The tcp port
- * differs from instance to instance, and is instance.host_port()
- *
- */
- adb_tunnel.AddParameter("--server=tcp");
- adb_tunnel.AddParameter("--vsock_port=6520");
- adb_tunnel.AddParameter(std::string{"--tcp_port="} +
- std::to_string(instance.host_port()));
- adb_tunnel.AddParameter(std::string{"--vsock_cid="} +
- std::to_string(instance.vsock_guest_cid()));
- process_monitor->AddCommand(std::move(adb_tunnel));
- }
- if (AdbVsockHalfTunnelEnabled(config)) {
- Command adb_tunnel(SocketVsockProxyBinary());
- adb_tunnel.AddParameter("-adbd_events_fd=", adbd_events_pipe);
- /*
- * This socket_vsock_proxy (a.k.a. sv proxy) runs on the host, and cooperates with
- * the adbd inside the guest. See this file:
- * shared/device.mk, especially the line says "persist.adb.tcp.port="
- *
- * The guest adbd is listening on vsock:cid:5555 across cuttlefish instances.
- * Sv proxy faces the host adb daemon via tcp. The server type should be therefore
- * tcp, and the port should differ from instance to instance and be equal to
- * instance.host_port()
- */
- adb_tunnel.AddParameter("--server=tcp");
- adb_tunnel.AddParameter(append("--vsock_port=", 5555));
- adb_tunnel.AddParameter(append("--tcp_port=", instance.host_port()));
- adb_tunnel.AddParameter(append("--vsock_cid=", instance.vsock_guest_cid()));
- process_monitor->AddCommand(std::move(adb_tunnel));
- }
-}
-
-void LaunchMetrics(ProcessMonitor* process_monitor) {
- Command metrics(MetricsBinary());
-
- process_monitor->AddCommand(std::move(metrics));
-}
-
-void LaunchGnssGrpcProxyServerIfEnabled(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor) {
- if (!config.enable_gnss_grpc_proxy() ||
- !FileExists(GnssGrpcProxyBinary())) {
- return;
+ command.AppendToLastParameter(event_pipe_write_ends_[i]);
+ }
}
+ return single_element_emplace(std::move(command));
+ }
+
+ // KernelLogPipeProvider
+ SharedFD KernelLogPipe() override {
+ CHECK(!event_pipe_read_ends_.empty()) << "No more kernel pipes left";
+ SharedFD ret = event_pipe_read_ends_.back();
+ event_pipe_read_ends_.pop_back();
+ return ret;
+ }
+
+ // Feature
+ bool Enabled() const override { return true; }
+ std::string Name() const override { return "KernelLogMonitor"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ auto log_name = instance_.kernel_log_pipe_name();
+ if (mkfifo(log_name.c_str(), 0600) != 0) {
+ LOG(ERROR) << "Unable to create named pipe at " << log_name << ": "
+ << strerror(errno);
+ return false;
+ }
+
+ // Open the pipe here (from the launcher) to ensure the pipe is not deleted
+ // due to the usage counters in the kernel reaching zero. If this is not
+ // done and the kernel_log_monitor crashes for some reason the VMM may get
+ // SIGPIPE.
+ fifo_ = SharedFD::Open(log_name, O_RDWR);
+ if (!fifo_->IsOpen()) {
+ LOG(ERROR) << "Unable to open \"" << log_name << "\"";
+ return false;
+ }
+
+ // TODO(schuffelen): Find a way to calculate this dynamically.
+ int number_of_event_pipes = 4;
+ if (number_of_event_pipes > 0) {
+ for (unsigned int i = 0; i < number_of_event_pipes; ++i) {
+ SharedFD event_pipe_write_end, event_pipe_read_end;
+ if (!SharedFD::Pipe(&event_pipe_read_end, &event_pipe_write_end)) {
+ PLOG(ERROR) << "Unable to create kernel log events pipe: ";
+ return false;
+ }
+ event_pipe_write_ends_.push_back(event_pipe_write_end);
+ event_pipe_read_ends_.push_back(event_pipe_read_end);
+ }
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ SharedFD fifo_;
+ std::vector<SharedFD> event_pipe_write_ends_;
+ std::vector<SharedFD> event_pipe_read_ends_;
+};
+
+class RootCanal : public CommandSource {
+ public:
+ INJECT(RootCanal(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ if (!Enabled()) {
+ return {};
+ }
+ Command command(RootCanalBinary());
+
+ // Test port
+ command.AddParameter(instance_.rootcanal_test_port());
+ // HCI server port
+ command.AddParameter(instance_.rootcanal_hci_port());
+ // Link server port
+ command.AddParameter(instance_.rootcanal_link_port());
+ // Bluetooth controller properties file
+ command.AddParameter("--controller_properties_file=",
+ instance_.rootcanal_config_file());
+ // Default commands file
+ command.AddParameter("--default_commands_file=",
+ instance_.rootcanal_default_commands_file());
+
+ return single_element_emplace(std::move(command));
+ }
+
+ // Feature
+ bool Enabled() const override { return config_.enable_host_bluetooth(); }
+ std::string Name() const override { return "RootCanal"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override { return true; }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+};
+
+class LogcatReceiver : public CommandSource, public DiagnosticInformation {
+ public:
+ INJECT(LogcatReceiver(const CuttlefishConfig::InstanceSpecific& instance))
+ : instance_(instance) {}
+ // DiagnosticInformation
+ std::vector<std::string> Diagnostics() const override {
+ return {"Logcat output: " + instance_.logcat_path()};
+ }
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command command(LogcatReceiverBinary());
+ command.AddParameter("-log_pipe_fd=", pipe_);
+ return single_element_emplace(std::move(command));
+ }
+
+ // Feature
+ bool Enabled() const override { return true; }
+ std::string Name() const override { return "LogcatReceiver"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ auto log_name = instance_.logcat_pipe_name();
+ if (mkfifo(log_name.c_str(), 0600) != 0) {
+ LOG(ERROR) << "Unable to create named pipe at " << log_name << ": "
+ << strerror(errno);
+ return false;
+ }
+ // Open the pipe here (from the launcher) to ensure the pipe is not deleted
+ // due to the usage counters in the kernel reaching zero. If this is not
+ // done and the logcat_receiver crashes for some reason the VMM may get
+ // SIGPIPE.
+ pipe_ = SharedFD::Open(log_name.c_str(), O_RDWR);
+ if (!pipe_->IsOpen()) {
+ LOG(ERROR) << "Can't open \"" << log_name << "\": " << pipe_->StrError();
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ SharedFD pipe_;
+};
+
+class ConfigServer : public CommandSource {
+ public:
+ INJECT(ConfigServer(const CuttlefishConfig::InstanceSpecific& instance))
+ : instance_(instance) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command cmd(ConfigServerBinary());
+ cmd.AddParameter("-server_fd=", socket_);
+ return single_element_emplace(std::move(cmd));
+ }
+
+ // Feature
+ bool Enabled() const override { return true; }
+ std::string Name() const override { return "ConfigServer"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ auto port = instance_.config_server_port();
+ socket_ = SharedFD::VsockServer(port, SOCK_STREAM);
+ if (!socket_->IsOpen()) {
+ LOG(ERROR) << "Unable to create configuration server socket: "
+ << socket_->StrError();
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ SharedFD socket_;
+};
+
+class TombstoneReceiver : public CommandSource {
+ public:
+ INJECT(TombstoneReceiver(const CuttlefishConfig::InstanceSpecific& instance))
+ : instance_(instance) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command cmd(TombstoneReceiverBinary());
+ cmd.AddParameter("-server_fd=", socket_);
+ cmd.AddParameter("-tombstone_dir=", tombstone_dir_);
+ return single_element_emplace(std::move(cmd));
+ }
+
+ // Feature
+ bool Enabled() const override { return true; }
+ std::string Name() const override { return "TombstoneReceiver"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ tombstone_dir_ = instance_.PerInstancePath("tombstones");
+ if (!DirectoryExists(tombstone_dir_.c_str())) {
+ LOG(DEBUG) << "Setting up " << tombstone_dir_;
+ if (mkdir(tombstone_dir_.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) <
+ 0) {
+ LOG(ERROR) << "Failed to create tombstone directory: " << tombstone_dir_
+ << ". Error: " << errno;
+ return false;
+ }
+ }
+
+ auto port = instance_.tombstone_receiver_port();
+ socket_ = SharedFD::VsockServer(port, SOCK_STREAM);
+ if (!socket_->IsOpen()) {
+ LOG(ERROR) << "Unable to create tombstone server socket: "
+ << socket_->StrError();
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ SharedFD socket_;
+ std::string tombstone_dir_;
+};
+
+class MetricsService : public CommandSource {
+ public:
+ INJECT(MetricsService(const CuttlefishConfig& config)) : config_(config) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ return single_element_emplace(Command(MetricsBinary()));
+ }
+
+ // Feature
+ bool Enabled() const override {
+ return config_.enable_metrics() == CuttlefishConfig::kYes;
+ }
+ std::string Name() const override { return "MetricsService"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override { return true; }
+
+ private:
+ const CuttlefishConfig& config_;
+};
+
+class GnssGrpcProxyServer : public CommandSource {
+ public:
+ INJECT(
+ GnssGrpcProxyServer(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
Command gnss_grpc_proxy_cmd(GnssGrpcProxyBinary());
- auto instance = config.ForDefaultInstance();
+ const unsigned gnss_grpc_proxy_server_port =
+ instance_.gnss_grpc_proxy_server_port();
+ gnss_grpc_proxy_cmd.AddParameter("--gnss_in_fd=", gnss_grpc_proxy_in_wr_);
+ gnss_grpc_proxy_cmd.AddParameter("--gnss_out_fd=", gnss_grpc_proxy_out_rd_);
+ gnss_grpc_proxy_cmd.AddParameter("--gnss_grpc_port=",
+ gnss_grpc_proxy_server_port);
+ if (!instance_.gnss_file_path().empty()) {
+ // If path is provided, proxy will start as local mode.
+ gnss_grpc_proxy_cmd.AddParameter("--gnss_file_path=",
+ instance_.gnss_file_path());
+ }
+ return single_element_emplace(std::move(gnss_grpc_proxy_cmd));
+ }
- auto gnss_in_pipe_name = instance.gnss_in_pipe_name();
+ // Feature
+ bool Enabled() const override {
+ return config_.enable_gnss_grpc_proxy() &&
+ FileExists(GnssGrpcProxyBinary());
+ }
+
+ std::string Name() const override { return "GnssGrpcProxyServer"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ auto gnss_in_pipe_name = instance_.gnss_in_pipe_name();
if (mkfifo(gnss_in_pipe_name.c_str(), 0600) != 0) {
auto error = errno;
LOG(ERROR) << "Failed to create gnss input fifo for crosvm: "
- << strerror(error);
- return;
+ << strerror(error);
+ return false;
}
- auto gnss_out_pipe_name = instance.gnss_out_pipe_name();
+ auto gnss_out_pipe_name = instance_.gnss_out_pipe_name();
if (mkfifo(gnss_out_pipe_name.c_str(), 0660) != 0) {
auto error = errno;
LOG(ERROR) << "Failed to create gnss output fifo for crosvm: "
- << strerror(error);
- return;
+ << strerror(error);
+ return false;
}
// These fds will only be read from or written to, but open them with
// read and write access to keep them open in case the subprocesses exit
- SharedFD gnss_grpc_proxy_in_wr =
- SharedFD::Open(gnss_in_pipe_name.c_str(), O_RDWR);
- if (!gnss_grpc_proxy_in_wr->IsOpen()) {
+ gnss_grpc_proxy_in_wr_ = SharedFD::Open(gnss_in_pipe_name.c_str(), O_RDWR);
+ if (!gnss_grpc_proxy_in_wr_->IsOpen()) {
LOG(ERROR) << "Failed to open gnss_grpc_proxy input fifo for writes: "
- << gnss_grpc_proxy_in_wr->StrError();
- return;
+ << gnss_grpc_proxy_in_wr_->StrError();
+ return false;
}
- SharedFD gnss_grpc_proxy_out_rd =
+ gnss_grpc_proxy_out_rd_ =
SharedFD::Open(gnss_out_pipe_name.c_str(), O_RDWR);
- if (!gnss_grpc_proxy_out_rd->IsOpen()) {
+ if (!gnss_grpc_proxy_out_rd_->IsOpen()) {
LOG(ERROR) << "Failed to open gnss_grpc_proxy output fifo for reads: "
- << gnss_grpc_proxy_out_rd->StrError();
- return;
+ << gnss_grpc_proxy_out_rd_->StrError();
+ return false;
}
-
- const unsigned gnss_grpc_proxy_server_port = instance.gnss_grpc_proxy_server_port();
- gnss_grpc_proxy_cmd.AddParameter("--gnss_in_fd=", gnss_grpc_proxy_in_wr);
- gnss_grpc_proxy_cmd.AddParameter("--gnss_out_fd=", gnss_grpc_proxy_out_rd);
- gnss_grpc_proxy_cmd.AddParameter("--gnss_grpc_port=", gnss_grpc_proxy_server_port);
- if (!instance.gnss_file_path().empty()) {
- // If path is provided, proxy will start as local mode.
- gnss_grpc_proxy_cmd.AddParameter("--gnss_file_path=", instance.gnss_file_path());
- }
- process_monitor->AddCommand(std::move(gnss_grpc_proxy_cmd));
-}
-
-void LaunchBluetoothConnector(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config) {
- auto instance = config.ForDefaultInstance();
- std::vector<std::string> fifo_paths = {
- instance.PerInstanceInternalPath("bt_fifo_vm.in"),
- instance.PerInstanceInternalPath("bt_fifo_vm.out"),
- };
- std::vector<SharedFD> fifos;
- for (const auto& path : fifo_paths) {
- unlink(path.c_str());
- if (mkfifo(path.c_str(), 0660) < 0) {
- PLOG(ERROR) << "Could not create " << path;
- return;
- }
- auto fd = SharedFD::Open(path, O_RDWR);
- if (!fd->IsOpen()) {
- LOG(ERROR) << "Could not open " << path << ": " << fd->StrError();
- return;
- }
- fifos.push_back(fd);
+ return true;
}
- Command command(DefaultHostArtifactsPath("bin/bt_connector"));
- command.AddParameter("-bt_out=", fifos[0]);
- command.AddParameter("-bt_in=", fifos[1]);
- command.AddParameter("-hci_port=", instance.rootcanal_hci_port());
- command.AddParameter("-link_port=", instance.rootcanal_link_port());
- command.AddParameter("-test_port=", instance.rootcanal_test_port());
- process_monitor->AddCommand(std::move(command));
-}
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ SharedFD gnss_grpc_proxy_in_wr_;
+ SharedFD gnss_grpc_proxy_out_rd_;
+};
-void LaunchSecureEnvironment(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config) {
- auto instance = config.ForDefaultInstance();
- std::vector<std::string> fifo_paths = {
- instance.PerInstanceInternalPath("keymaster_fifo_vm.in"),
- instance.PerInstanceInternalPath("keymaster_fifo_vm.out"),
- instance.PerInstanceInternalPath("gatekeeper_fifo_vm.in"),
- instance.PerInstanceInternalPath("gatekeeper_fifo_vm.out"),
- };
- std::vector<SharedFD> fifos;
- for (const auto& path : fifo_paths) {
- unlink(path.c_str());
- if (mkfifo(path.c_str(), 0600) < 0) {
- PLOG(ERROR) << "Could not create " << path;
- return;
- }
- auto fd = SharedFD::Open(path, O_RDWR);
- if (!fd->IsOpen()) {
- LOG(ERROR) << "Could not open " << path << ": " << fd->StrError();
- return;
- }
- fifos.push_back(fd);
+class BluetoothConnector : public CommandSource {
+ public:
+ INJECT(BluetoothConnector(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command command(DefaultHostArtifactsPath("bin/bt_connector"));
+ command.AddParameter("-bt_out=", fifos_[0]);
+ command.AddParameter("-bt_in=", fifos_[1]);
+ command.AddParameter("-hci_port=", instance_.rootcanal_hci_port());
+ command.AddParameter("-link_port=", instance_.rootcanal_link_port());
+ command.AddParameter("-test_port=", instance_.rootcanal_test_port());
+ return single_element_emplace(std::move(command));
}
- Command command(HostBinaryPath("secure_env"));
- command.AddParameter("-keymaster_fd_out=", fifos[0]);
- command.AddParameter("-keymaster_fd_in=", fifos[1]);
- command.AddParameter("-gatekeeper_fd_out=", fifos[2]);
- command.AddParameter("-gatekeeper_fd_in=", fifos[3]);
+ // Feature
+ bool Enabled() const override { return config_.enable_host_bluetooth(); }
- const auto& secure_hals = config.secure_hals();
- bool secure_keymint = secure_hals.count(SecureHal::Keymint) > 0;
- command.AddParameter("-keymint_impl=", secure_keymint ? "tpm" : "software");
- bool secure_gatekeeper = secure_hals.count(SecureHal::Gatekeeper) > 0;
- auto gatekeeper_impl = secure_gatekeeper ? "tpm" : "software";
- command.AddParameter("-gatekeeper_impl=", gatekeeper_impl);
+ std::string Name() const override { return "BluetoothConnector"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
- process_monitor->AddCommand(std::move(command));
-}
-
-void LaunchCustomActionServers(Command& webrtc_cmd,
- ProcessMonitor* process_monitor,
- const CuttlefishConfig& config) {
- bool first = true;
- for (const auto& custom_action : config.custom_actions()) {
- if (custom_action.server) {
- // Create a socket pair that will be used for communication between
- // WebRTC and the action server.
- SharedFD webrtc_socket, action_server_socket;
- if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0,
- &webrtc_socket, &action_server_socket)) {
- LOG(ERROR) << "Unable to create custom action server socket pair: "
- << strerror(errno);
- continue;
+ protected:
+ bool Setup() override {
+ std::vector<std::string> fifo_paths = {
+ instance_.PerInstanceInternalPath("bt_fifo_vm.in"),
+ instance_.PerInstanceInternalPath("bt_fifo_vm.out"),
+ };
+ for (const auto& path : fifo_paths) {
+ unlink(path.c_str());
+ if (mkfifo(path.c_str(), 0660) < 0) {
+ PLOG(ERROR) << "Could not create " << path;
+ return false;
}
-
- // Launch the action server, providing its socket pair fd as the only argument.
- std::string binary = "bin/" + *(custom_action.server);
- Command command(DefaultHostArtifactsPath(binary));
- command.AddParameter(action_server_socket);
- process_monitor->AddCommand(std::move(command));
-
- // Pass the WebRTC socket pair fd to WebRTC.
- if (first) {
- first = false;
- webrtc_cmd.AddParameter("-action_servers=", *custom_action.server, ":", webrtc_socket);
- } else {
- webrtc_cmd.AppendToLastParameter(",", *custom_action.server, ":", webrtc_socket);
+ auto fd = SharedFD::Open(path, O_RDWR);
+ if (!fd->IsOpen()) {
+ LOG(ERROR) << "Could not open " << path << ": " << fd->StrError();
+ return false;
}
+ fifos_.push_back(fd);
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ std::vector<SharedFD> fifos_;
+};
+
+class SecureEnvironment : public CommandSource {
+ public:
+ INJECT(SecureEnvironment(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance,
+ KernelLogPipeProvider& kernel_log_pipe_provider))
+ : config_(config),
+ instance_(instance),
+ kernel_log_pipe_provider_(kernel_log_pipe_provider) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command command(HostBinaryPath("secure_env"));
+ command.AddParameter("-keymaster_fd_out=", fifos_[0]);
+ command.AddParameter("-keymaster_fd_in=", fifos_[1]);
+ command.AddParameter("-gatekeeper_fd_out=", fifos_[2]);
+ command.AddParameter("-gatekeeper_fd_in=", fifos_[3]);
+
+ const auto& secure_hals = config_.secure_hals();
+ bool secure_keymint = secure_hals.count(SecureHal::Keymint) > 0;
+ command.AddParameter("-keymint_impl=", secure_keymint ? "tpm" : "software");
+ bool secure_gatekeeper = secure_hals.count(SecureHal::Gatekeeper) > 0;
+ auto gatekeeper_impl = secure_gatekeeper ? "tpm" : "software";
+ command.AddParameter("-gatekeeper_impl=", gatekeeper_impl);
+
+ command.AddParameter("-kernel_events_fd=", kernel_log_pipe_);
+
+ return single_element_emplace(std::move(command));
+ }
+
+ // Feature
+ bool Enabled() const override { return config_.enable_host_bluetooth(); }
+ std::string Name() const override { return "SecureEnvironment"; }
+ std::unordered_set<Feature*> Dependencies() const override {
+ return {&kernel_log_pipe_provider_};
+ }
+
+ protected:
+ bool Setup() override {
+ std::vector<std::string> fifo_paths = {
+ instance_.PerInstanceInternalPath("keymaster_fifo_vm.in"),
+ instance_.PerInstanceInternalPath("keymaster_fifo_vm.out"),
+ instance_.PerInstanceInternalPath("gatekeeper_fifo_vm.in"),
+ instance_.PerInstanceInternalPath("gatekeeper_fifo_vm.out"),
+ };
+ std::vector<SharedFD> fifos;
+ for (const auto& path : fifo_paths) {
+ unlink(path.c_str());
+ if (mkfifo(path.c_str(), 0600) < 0) {
+ PLOG(ERROR) << "Could not create " << path;
+ return false;
+ }
+ auto fd = SharedFD::Open(path, O_RDWR);
+ if (!fd->IsOpen()) {
+ LOG(ERROR) << "Could not open " << path << ": " << fd->StrError();
+ return false;
+ }
+ fifos_.push_back(fd);
+ }
+
+ kernel_log_pipe_ = kernel_log_pipe_provider_.KernelLogPipe();
+
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ std::vector<SharedFD> fifos_;
+ KernelLogPipeProvider& kernel_log_pipe_provider_;
+ SharedFD kernel_log_pipe_;
+};
+
+class VehicleHalServer : public CommandSource {
+ public:
+ INJECT(VehicleHalServer(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command grpc_server(VehicleHalGrpcServerBinary());
+
+ const unsigned vhal_server_cid = 2;
+ const unsigned vhal_server_port = instance_.vehicle_hal_server_port();
+ const std::string vhal_server_power_state_file =
+ AbsolutePath(instance_.PerInstancePath("power_state"));
+ const std::string vhal_server_power_state_socket =
+ AbsolutePath(instance_.PerInstancePath("power_state_socket"));
+
+ grpc_server.AddParameter("--server_cid=", vhal_server_cid);
+ grpc_server.AddParameter("--server_port=", vhal_server_port);
+ grpc_server.AddParameter("--power_state_file=",
+ vhal_server_power_state_file);
+ grpc_server.AddParameter("--power_state_socket=",
+ vhal_server_power_state_socket);
+ return single_element_emplace(std::move(grpc_server));
+ }
+
+ // Feature
+ bool Enabled() const override {
+ return config_.enable_vehicle_hal_grpc_server() &&
+ FileExists(VehicleHalGrpcServerBinary());
+ }
+ std::string Name() const override { return "VehicleHalServer"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override { return true; }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+};
+
+class ConsoleForwarder : public CommandSource, public DiagnosticInformation {
+ public:
+ INJECT(ConsoleForwarder(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+ // DiagnosticInformation
+ std::vector<std::string> Diagnostics() const override {
+ if (Enabled()) {
+ return {"To access the console run: screen " + instance_.console_path()};
+ } else {
+ return {"Serial console is disabled; use -console=true to enable it."};
}
}
-}
-void LaunchVehicleHalServerIfEnabled(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor) {
- if (!config.enable_vehicle_hal_grpc_server() ||
- !FileExists(config.vehicle_hal_grpc_server_binary())) {
- return;
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command console_forwarder_cmd(ConsoleForwarderBinary());
+
+ console_forwarder_cmd.AddParameter("--console_in_fd=",
+ console_forwarder_in_wr_);
+ console_forwarder_cmd.AddParameter("--console_out_fd=",
+ console_forwarder_out_rd_);
+ return single_element_emplace(std::move(console_forwarder_cmd));
}
- Command grpc_server(config.vehicle_hal_grpc_server_binary());
- auto instance = config.ForDefaultInstance();
+ // Feature
+ bool Enabled() const override { return config_.console(); }
+ std::string Name() const override { return "ConsoleForwarder"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
- const unsigned vhal_server_cid = 2;
- const unsigned vhal_server_port = instance.vehicle_hal_server_port();
- const std::string vhal_server_power_state_file =
- AbsolutePath(instance.PerInstancePath("power_state"));
- const std::string vhal_server_power_state_socket =
- AbsolutePath(instance.PerInstancePath("power_state_socket"));
+ protected:
+ bool Setup() override {
+ auto console_in_pipe_name = instance_.console_in_pipe_name();
+ if (mkfifo(console_in_pipe_name.c_str(), 0600) != 0) {
+ auto error = errno;
+ LOG(ERROR) << "Failed to create console input fifo for crosvm: "
+ << strerror(error);
+ return false;
+ }
- grpc_server.AddParameter("--server_cid=", vhal_server_cid);
- grpc_server.AddParameter("--server_port=", vhal_server_port);
- grpc_server.AddParameter("--power_state_file=", vhal_server_power_state_file);
- grpc_server.AddParameter("--power_state_socket=", vhal_server_power_state_socket);
- process_monitor->AddCommand(std::move(grpc_server));
-}
+ auto console_out_pipe_name = instance_.console_out_pipe_name();
+ if (mkfifo(console_out_pipe_name.c_str(), 0660) != 0) {
+ auto error = errno;
+ LOG(ERROR) << "Failed to create console output fifo for crosvm: "
+ << strerror(error);
+ return false;
+ }
-void LaunchConsoleForwarderIfEnabled(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor)
-{
- if (!config.console()) {
- return;
+ // These fds will only be read from or written to, but open them with
+ // read and write access to keep them open in case the subprocesses exit
+ console_forwarder_in_wr_ =
+ SharedFD::Open(console_in_pipe_name.c_str(), O_RDWR);
+ if (!console_forwarder_in_wr_->IsOpen()) {
+ LOG(ERROR) << "Failed to open console_forwarder input fifo for writes: "
+ << console_forwarder_in_wr_->StrError();
+ return false;
+ }
+
+ console_forwarder_out_rd_ =
+ SharedFD::Open(console_out_pipe_name.c_str(), O_RDWR);
+ if (!console_forwarder_out_rd_->IsOpen()) {
+ LOG(ERROR) << "Failed to open console_forwarder output fifo for reads: "
+ << console_forwarder_out_rd_->StrError();
+ return false;
+ }
+ return true;
}
- Command console_forwarder_cmd(ConsoleForwarderBinary());
- auto instance = config.ForDefaultInstance();
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ SharedFD console_forwarder_in_wr_;
+ SharedFD console_forwarder_out_rd_;
+};
- auto console_in_pipe_name = instance.console_in_pipe_name();
- if (mkfifo(console_in_pipe_name.c_str(), 0600) != 0) {
- auto error = errno;
- LOG(ERROR) << "Failed to create console input fifo for crosvm: "
- << strerror(error);
- return;
- }
-
- auto console_out_pipe_name = instance.console_out_pipe_name();
- if (mkfifo(console_out_pipe_name.c_str(), 0660) != 0) {
- auto error = errno;
- LOG(ERROR) << "Failed to create console output fifo for crosvm: "
- << strerror(error);
- return;
- }
-
- // These fds will only be read from or written to, but open them with
- // read and write access to keep them open in case the subprocesses exit
- SharedFD console_forwarder_in_wr =
- SharedFD::Open(console_in_pipe_name.c_str(), O_RDWR);
- if (!console_forwarder_in_wr->IsOpen()) {
- LOG(ERROR) << "Failed to open console_forwarder input fifo for writes: "
- << console_forwarder_in_wr->StrError();
- return;
- }
-
- SharedFD console_forwarder_out_rd =
- SharedFD::Open(console_out_pipe_name.c_str(), O_RDWR);
- if (!console_forwarder_out_rd->IsOpen()) {
- LOG(ERROR) << "Failed to open console_forwarder output fifo for reads: "
- << console_forwarder_out_rd->StrError();
- return;
- }
-
- console_forwarder_cmd.AddParameter("--console_in_fd=", console_forwarder_in_wr);
- console_forwarder_cmd.AddParameter("--console_out_fd=", console_forwarder_out_rd);
- process_monitor->AddCommand(std::move(console_forwarder_cmd));
+using PublicDeps = fruit::Required<const CuttlefishConfig,
+ const CuttlefishConfig::InstanceSpecific>;
+fruit::Component<PublicDeps, KernelLogPipeProvider> launchComponent() {
+ using InternalDeps = fruit::Required<const CuttlefishConfig,
+ const CuttlefishConfig::InstanceSpecific,
+ KernelLogPipeProvider>;
+ using Multi = Multibindings<InternalDeps>;
+ using Bases = Multi::Bases<CommandSource, DiagnosticInformation, Feature>;
+ return fruit::createComponent()
+ .bind<KernelLogPipeProvider, KernelLogMonitor>()
+ .install(Bases::Impls<BluetoothConnector>)
+ .install(Bases::Impls<ConfigServer>)
+ .install(Bases::Impls<ConsoleForwarder>)
+ .install(Bases::Impls<GnssGrpcProxyServer>)
+ .install(Bases::Impls<KernelLogMonitor>)
+ .install(Bases::Impls<LogcatReceiver>)
+ .install(Bases::Impls<MetricsService>)
+ .install(Bases::Impls<RootCanal>)
+ .install(Bases::Impls<SecureEnvironment>)
+ .install(Bases::Impls<TombstoneReceiver>)
+ .install(Bases::Impls<VehicleHalServer>);
}
} // namespace cuttlefish
diff --git a/host/commands/run_cvd/launch.h b/host/commands/run_cvd/launch.h
index f265bf3..2cfcb33 100644
--- a/host/commands/run_cvd/launch.h
+++ b/host/commands/run_cvd/launch.h
@@ -1,63 +1,58 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
#pragma once
-#include <functional>
-#include <set>
+#include <fruit/fruit.h>
#include <string>
+#include <vector>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/subprocess.h"
-#include "host/commands/run_cvd/process_monitor.h"
#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/feature.h"
namespace cuttlefish {
-std::vector<SharedFD> LaunchKernelLogMonitor(
- const CuttlefishConfig& config,
- ProcessMonitor* process_monitor,
- unsigned int number_of_event_pipes);
-void LaunchAdbConnectorIfEnabled(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config);
-void LaunchSocketVsockProxyIfEnabled(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config,
- SharedFD adbd_events_pipe);
-void LaunchModemSimulatorIfEnabled(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor);
+class CommandSource : public virtual Feature {
+ public:
+ virtual ~CommandSource();
+ virtual std::vector<Command> Commands() = 0;
+};
-void LaunchVNCServer(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor);
+class KernelLogPipeProvider : public virtual Feature {
+ public:
+ virtual ~KernelLogPipeProvider();
+ virtual SharedFD KernelLogPipe() = 0;
+};
-void LaunchTombstoneReceiver(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor);
-void LaunchRootCanal(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor);
-void LaunchLogcatReceiver(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor);
-void LaunchConfigServer(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor);
+fruit::Component<fruit::Required<const CuttlefishConfig,
+ const CuttlefishConfig::InstanceSpecific>,
+ KernelLogPipeProvider>
+launchComponent();
-void LaunchWebRTC(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config,
- SharedFD kernel_log_events_pipe);
+fruit::Component<fruit::Required<const CuttlefishConfig,
+ const CuttlefishConfig::InstanceSpecific>>
+launchModemComponent();
-void LaunchMetrics(ProcessMonitor* process_monitor);
+fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
+ const CuttlefishConfig::InstanceSpecific>>
+launchAdbComponent();
-void LaunchGnssGrpcProxyServerIfEnabled(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor);
-
-void LaunchSecureEnvironment(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config);
-
-void LaunchBluetoothConnector(ProcessMonitor* process_monitor,
- const CuttlefishConfig& config);
-
-void LaunchCustomActionServers(Command& webrtc_cmd,
- ProcessMonitor* process_monitor,
- const CuttlefishConfig& config);
-
-void LaunchVehicleHalServerIfEnabled(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor);
-
-void LaunchConsoleForwarderIfEnabled(const CuttlefishConfig& config,
- ProcessMonitor* process_monitor);
+fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
+ const CuttlefishConfig::InstanceSpecific>>
+launchStreamerComponent();
} // namespace cuttlefish
diff --git a/host/commands/run_cvd/launch_adb.cpp b/host/commands/run_cvd/launch_adb.cpp
new file mode 100644
index 0000000..7dbc55c
--- /dev/null
+++ b/host/commands/run_cvd/launch_adb.cpp
@@ -0,0 +1,219 @@
+//
+// 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/commands/run_cvd/launch.h"
+
+#include <android-base/logging.h>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/subprocess.h"
+#include "host/libs/config/adb_config.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/known_paths.h"
+
+namespace cuttlefish {
+
+namespace {
+
+class AdbHelper {
+ public:
+ INJECT(AdbHelper(const CuttlefishConfig::InstanceSpecific& instance,
+ const AdbConfig& config))
+ : instance_(instance), config_(config) {}
+
+ bool ModeEnabled(const AdbMode& mode) const {
+ return config_.adb_mode().count(mode) > 0;
+ }
+
+ std::string ConnectorTcpArg() const {
+ return "0.0.0.0:" + std::to_string(instance_.adb_host_port());
+ }
+
+ std::string ConnectorVsockArg() const {
+ return "vsock:" + std::to_string(instance_.vsock_guest_cid()) + ":5555";
+ }
+
+ bool VsockTunnelEnabled() const {
+ return instance_.vsock_guest_cid() > 2 && ModeEnabled(AdbMode::VsockTunnel);
+ }
+
+ bool VsockHalfTunnelEnabled() const {
+ return instance_.vsock_guest_cid() > 2 &&
+ ModeEnabled(AdbMode::VsockHalfTunnel);
+ }
+
+ bool TcpConnectorEnabled() const {
+ bool vsock_tunnel = VsockTunnelEnabled();
+ bool vsock_half_tunnel = VsockHalfTunnelEnabled();
+ return config_.run_adb_connector() && (vsock_tunnel || vsock_half_tunnel);
+ }
+
+ bool VsockConnectorEnabled() const {
+ return config_.run_adb_connector() && ModeEnabled(AdbMode::NativeVsock);
+ }
+
+ private:
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ const AdbConfig& config_;
+};
+
+class AdbConnector : public CommandSource {
+ public:
+ INJECT(AdbConnector(const AdbHelper& helper)) : helper_(helper) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command console_forwarder_cmd(ConsoleForwarderBinary());
+ Command adb_connector(AdbConnectorBinary());
+ std::set<std::string> addresses;
+
+ if (helper_.TcpConnectorEnabled()) {
+ addresses.insert(helper_.ConnectorTcpArg());
+ }
+ if (helper_.VsockConnectorEnabled()) {
+ addresses.insert(helper_.ConnectorVsockArg());
+ }
+
+ if (addresses.size() == 0) {
+ return {};
+ }
+ std::string address_arg = "--addresses=";
+ for (auto& arg : addresses) {
+ address_arg += arg + ",";
+ }
+ address_arg.pop_back();
+ adb_connector.AddParameter(address_arg);
+ std::vector<Command> commands;
+ commands.emplace_back(std::move(adb_connector));
+ return std::move(commands);
+ }
+
+ // Feature
+ bool Enabled() const override {
+ return helper_.TcpConnectorEnabled() || helper_.VsockConnectorEnabled();
+ }
+ std::string Name() const override { return "AdbConnector"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override { return true; }
+
+ private:
+ const AdbHelper& helper_;
+};
+
+class SocketVsockProxy : public CommandSource {
+ public:
+ INJECT(SocketVsockProxy(const AdbHelper& helper,
+ const CuttlefishConfig::InstanceSpecific& instance,
+ KernelLogPipeProvider& log_pipe_provider))
+ : helper_(helper),
+ instance_(instance),
+ log_pipe_provider_(log_pipe_provider) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ std::vector<Command> commands;
+ if (helper_.VsockTunnelEnabled()) {
+ Command adb_tunnel(SocketVsockProxyBinary());
+ adb_tunnel.AddParameter("-adbd_events_fd=", kernel_log_pipe_);
+ /**
+ * This socket_vsock_proxy (a.k.a. sv proxy) runs on the host. It assumes
+ * that another sv proxy runs inside the guest. see:
+ * shared/config/init.vendor.rc The sv proxy in the guest exposes
+ * vsock:cid:6520 across the cuttlefish instances in multi-tenancy. cid is
+ * different per instance.
+ *
+ * This host sv proxy should cooperate with the guest sv proxy. Thus, one
+ * end of the tunnel is vsock:cid:6520 regardless of instance number.
+ * Another end faces the host adb daemon via tcp. Thus, the server type is
+ * tcp here. The tcp port differs from instance to instance, and is
+ * instance.adb_host_port()
+ *
+ */
+ adb_tunnel.AddParameter("--server=tcp");
+ adb_tunnel.AddParameter("--vsock_port=6520");
+ adb_tunnel.AddParameter("--server_fd=", tcp_server_);
+ adb_tunnel.AddParameter("--vsock_cid=", instance_.vsock_guest_cid());
+ commands.emplace_back(std::move(adb_tunnel));
+ }
+ if (helper_.VsockHalfTunnelEnabled()) {
+ Command adb_tunnel(SocketVsockProxyBinary());
+ adb_tunnel.AddParameter("-adbd_events_fd=", kernel_log_pipe_);
+ /*
+ * This socket_vsock_proxy (a.k.a. sv proxy) runs on the host, and
+ * cooperates with the adbd inside the guest. See this file:
+ * shared/device.mk, especially the line says "persist.adb.tcp.port="
+ *
+ * The guest adbd is listening on vsock:cid:5555 across cuttlefish
+ * instances. Sv proxy faces the host adb daemon via tcp. The server type
+ * should be therefore tcp, and the port should differ from instance to
+ * instance and be equal to instance.adb_host_port()
+ */
+ adb_tunnel.AddParameter("--server=tcp");
+ adb_tunnel.AddParameter("--vsock_port=", 5555);
+ adb_tunnel.AddParameter("--server_fd=", tcp_server_);
+ adb_tunnel.AddParameter("--vsock_cid=", instance_.vsock_guest_cid());
+ commands.emplace_back(std::move(adb_tunnel));
+ }
+ return commands;
+ }
+
+ // Feature
+ bool Enabled() const override {
+ return helper_.VsockTunnelEnabled() || helper_.VsockHalfTunnelEnabled();
+ }
+ std::string Name() const override { return "SocketVsockProxy"; }
+ std::unordered_set<Feature*> Dependencies() const override {
+ return {static_cast<Feature*>(&log_pipe_provider_)};
+ }
+
+ protected:
+ bool Setup() override {
+ tcp_server_ =
+ SharedFD::SocketLocalServer(instance_.adb_host_port(), SOCK_STREAM);
+ if (!tcp_server_->IsOpen()) {
+ LOG(ERROR) << "Unable to create socket_vsock_proxy server socket: "
+ << tcp_server_->StrError();
+ return false;
+ }
+ kernel_log_pipe_ = log_pipe_provider_.KernelLogPipe();
+ return true;
+ }
+
+ private:
+ const AdbHelper& helper_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ KernelLogPipeProvider& log_pipe_provider_;
+ SharedFD kernel_log_pipe_;
+ SharedFD tcp_server_;
+};
+
+} // namespace
+
+fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
+ const CuttlefishConfig::InstanceSpecific>>
+launchAdbComponent() {
+ return fruit::createComponent()
+ .addMultibinding<CommandSource, AdbConnector>()
+ .addMultibinding<CommandSource, SocketVsockProxy>()
+ .addMultibinding<Feature, AdbConnector>()
+ .addMultibinding<Feature, SocketVsockProxy>();
+}
+
+} // namespace cuttlefish
diff --git a/host/commands/run_cvd/launch_modem.cpp b/host/commands/run_cvd/launch_modem.cpp
new file mode 100644
index 0000000..0bf02bc
--- /dev/null
+++ b/host/commands/run_cvd/launch_modem.cpp
@@ -0,0 +1,146 @@
+//
+// 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/commands/run_cvd/launch.h"
+
+#include <android-base/logging.h>
+#include <string.h>
+#include <sstream>
+#include <string>
+#include <unordered_set>
+#include <utility>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/subprocess.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/known_paths.h"
+
+namespace cuttlefish {
+
+static bool StopModemSimulator(int id) {
+ std::string socket_name = "modem_simulator" + std::to_string(id);
+ auto monitor_sock =
+ SharedFD::SocketLocalClient(socket_name, true, SOCK_STREAM);
+ if (!monitor_sock->IsOpen()) {
+ LOG(ERROR) << "The connection to modem simulator is closed";
+ return false;
+ }
+ std::string msg("STOP");
+ if (monitor_sock->Write(msg.data(), msg.size()) < 0) {
+ monitor_sock->Close();
+ LOG(ERROR) << "Failed to send 'STOP' to modem simulator";
+ return false;
+ }
+ char buf[64] = {0};
+ if (monitor_sock->Read(buf, sizeof(buf)) <= 0) {
+ monitor_sock->Close();
+ LOG(ERROR) << "Failed to read message from modem simulator";
+ return false;
+ }
+ if (strcmp(buf, "OK")) {
+ monitor_sock->Close();
+ LOG(ERROR) << "Read '" << buf << "' instead of 'OK' from modem simulator";
+ return false;
+ }
+
+ return true;
+}
+
+class ModemSimulator : public CommandSource {
+ public:
+ INJECT(ModemSimulator(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command cmd(ModemSimulatorBinary(), [this](Subprocess* proc) {
+ auto stopped = StopModemSimulator(instance_.modem_simulator_host_id());
+ if (stopped) {
+ return StopperResult::kStopSuccess;
+ }
+ LOG(WARNING) << "Failed to stop modem simulator nicely, "
+ << "attempting to KILL";
+ return KillSubprocess(proc) == StopperResult::kStopSuccess
+ ? StopperResult::kStopCrash
+ : StopperResult::kStopFailure;
+ });
+
+ auto sim_type = config_.modem_simulator_sim_type();
+ cmd.AddParameter(std::string{"-sim_type="} + std::to_string(sim_type));
+ cmd.AddParameter("-server_fds=");
+ bool first_socket = true;
+ for (const auto& socket : sockets_) {
+ if (!first_socket) {
+ cmd.AppendToLastParameter(",");
+ }
+ cmd.AppendToLastParameter(socket);
+ first_socket = false;
+ }
+
+ std::vector<Command> commands;
+ commands.emplace_back(std::move(cmd));
+ return commands;
+ }
+
+ // Feature
+ bool Enabled() const override {
+ if (!config_.enable_modem_simulator()) {
+ LOG(DEBUG) << "Modem simulator not enabled";
+ }
+ return config_.enable_modem_simulator();
+ }
+ std::string Name() const override { return "ModemSimulator"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ int instance_number = config_.modem_simulator_instance_number();
+ if (instance_number > 3 /* max value */ || instance_number < 0) {
+ LOG(ERROR)
+ << "Modem simulator instance number should range between 1 and 3";
+ return false;
+ }
+ auto ports = instance_.modem_simulator_ports();
+ for (int i = 0; i < instance_number; ++i) {
+ auto pos = ports.find(',');
+ auto temp = (pos != std::string::npos) ? ports.substr(0, pos) : ports;
+ auto port = std::stoi(temp);
+ ports = ports.substr(pos + 1);
+
+ auto socket = SharedFD::VsockServer(port, SOCK_STREAM);
+ CHECK(socket->IsOpen())
+ << "Unable to create modem simulator server socket: "
+ << socket->StrError();
+ sockets_.push_back(socket);
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ std::vector<SharedFD> sockets_;
+};
+
+fruit::Component<fruit::Required<const CuttlefishConfig,
+ const CuttlefishConfig::InstanceSpecific>>
+launchModemComponent() {
+ return fruit::createComponent()
+ .addMultibinding<CommandSource, ModemSimulator>()
+ .addMultibinding<Feature, ModemSimulator>();
+}
+
+} // namespace cuttlefish
diff --git a/host/commands/run_cvd/launch_streamer.cpp b/host/commands/run_cvd/launch_streamer.cpp
new file mode 100644
index 0000000..c7b83eb
--- /dev/null
+++ b/host/commands/run_cvd/launch_streamer.cpp
@@ -0,0 +1,365 @@
+//
+// 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/commands/run_cvd/launch.h"
+
+#include <android-base/logging.h>
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include "common/libs/fs/shared_buf.h"
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/files.h"
+#include "host/commands/run_cvd/reporting.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/known_paths.h"
+#include "host/libs/vm_manager/crosvm_manager.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+
+namespace cuttlefish {
+
+namespace {
+
+SharedFD CreateUnixInputServer(const std::string& path) {
+ auto server =
+ SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
+ if (!server->IsOpen()) {
+ LOG(ERROR) << "Unable to create unix input server: " << server->StrError();
+ return {};
+ }
+ return server;
+}
+
+std::vector<Command> LaunchCustomActionServers(Command& webrtc_cmd,
+ const CuttlefishConfig& config) {
+ bool first = true;
+ std::vector<Command> commands;
+ for (const auto& custom_action : config.custom_actions()) {
+ if (custom_action.server) {
+ // Create a socket pair that will be used for communication between
+ // WebRTC and the action server.
+ SharedFD webrtc_socket, action_server_socket;
+ if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &webrtc_socket,
+ &action_server_socket)) {
+ LOG(ERROR) << "Unable to create custom action server socket pair: "
+ << strerror(errno);
+ continue;
+ }
+
+ // Launch the action server, providing its socket pair fd as the only
+ // argument.
+ std::string binary = "bin/" + *(custom_action.server);
+ Command command(DefaultHostArtifactsPath(binary));
+ command.AddParameter(action_server_socket);
+ commands.emplace_back(std::move(command));
+
+ // Pass the WebRTC socket pair fd to WebRTC.
+ if (first) {
+ first = false;
+ webrtc_cmd.AddParameter("-action_servers=", *custom_action.server, ":",
+ webrtc_socket);
+ } else {
+ webrtc_cmd.AppendToLastParameter(",", *custom_action.server, ":",
+ webrtc_socket);
+ }
+ }
+ }
+ return commands;
+}
+
+// Creates the frame and input sockets and add the relevant arguments to the vnc
+// server and webrtc commands
+class StreamerSockets : public virtual Feature {
+ public:
+ INJECT(StreamerSockets(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+
+ void AppendCommandArguments(Command& cmd) {
+ if (config_.vm_manager() == vm_manager::QemuManager::name()) {
+ cmd.AddParameter("-write_virtio_input");
+ }
+ if (!touch_servers_.empty()) {
+ cmd.AddParameter("-touch_fds=", touch_servers_[0]);
+ for (int i = 1; i < touch_servers_.size(); ++i) {
+ cmd.AppendToLastParameter(",", touch_servers_[i]);
+ }
+ }
+ cmd.AddParameter("-keyboard_fd=", keyboard_server_);
+ cmd.AddParameter("-frame_server_fd=", frames_server_);
+ if (config_.enable_audio()) {
+ cmd.AddParameter("--audio_server_fd=", audio_server_);
+ }
+ }
+
+ // Feature
+ bool Enabled() const override {
+ bool is_qemu = config_.vm_manager() == vm_manager::QemuManager::name();
+ bool is_accelerated = config_.gpu_mode() != kGpuModeGuestSwiftshader;
+ return !(is_qemu && is_accelerated);
+ }
+ std::string Name() const override { return "StreamerSockets"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ auto use_vsockets = config_.vm_manager() == vm_manager::QemuManager::name();
+ for (int i = 0; i < config_.display_configs().size(); ++i) {
+ touch_servers_.push_back(
+ use_vsockets ? SharedFD::VsockServer(instance_.touch_server_port(),
+ SOCK_STREAM)
+ : CreateUnixInputServer(instance_.touch_socket_path(i)));
+ if (!touch_servers_.back()->IsOpen()) {
+ LOG(ERROR) << "Could not open touch server: "
+ << touch_servers_.back()->StrError();
+ return false;
+ }
+ }
+ if (use_vsockets) {
+ keyboard_server_ =
+ SharedFD::VsockServer(instance_.keyboard_server_port(), SOCK_STREAM);
+ } else {
+ keyboard_server_ =
+ CreateUnixInputServer(instance_.keyboard_socket_path());
+ }
+ if (!keyboard_server_->IsOpen()) {
+ LOG(ERROR) << "Failed to open keyboard server"
+ << keyboard_server_->StrError();
+ return false;
+ }
+ frames_server_ = CreateUnixInputServer(instance_.frames_socket_path());
+ if (!frames_server_->IsOpen()) {
+ LOG(ERROR) << "Could not open frames server: "
+ << frames_server_->StrError();
+ return false;
+ }
+ // TODO(schuffelen): Make this a separate optional feature?
+ if (config_.enable_audio()) {
+ auto path = config_.ForDefaultInstance().audio_server_path();
+ audio_server_ =
+ SharedFD::SocketLocalServer(path, false, SOCK_SEQPACKET, 0666);
+ if (!audio_server_->IsOpen()) {
+ LOG(ERROR) << "Could not create audio server: "
+ << audio_server_->StrError();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ std::vector<SharedFD> touch_servers_;
+ SharedFD keyboard_server_;
+ SharedFD frames_server_;
+ SharedFD audio_server_;
+};
+
+class VncServer : public virtual CommandSource, public DiagnosticInformation {
+ public:
+ INJECT(VncServer(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance,
+ StreamerSockets& sockets))
+ : config_(config), instance_(instance), sockets_(sockets) {}
+ // DiagnosticInformation
+ std::vector<std::string> Diagnostics() const override {
+ if (!Enabled()) {
+ return {};
+ }
+ std::ostringstream out;
+ out << "VNC server started on port "
+ << config_.ForDefaultInstance().vnc_server_port();
+ return {out.str()};
+ }
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ Command vnc_server(VncServerBinary());
+ vnc_server.AddParameter("-port=", instance_.vnc_server_port());
+ sockets_.AppendCommandArguments(vnc_server);
+
+ std::vector<Command> commands;
+ commands.emplace_back(std::move(vnc_server));
+ return commands;
+ }
+
+ // Feature
+ bool Enabled() const override {
+ return sockets_.Enabled() && config_.enable_vnc_server();
+ }
+ std::string Name() const override { return "VncServer"; }
+ std::unordered_set<Feature*> Dependencies() const override {
+ return {static_cast<Feature*>(&sockets_)};
+ }
+
+ protected:
+ bool Setup() override { return true; }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ StreamerSockets& sockets_;
+};
+
+class WebRtcServer : public virtual CommandSource,
+ public DiagnosticInformation {
+ public:
+ INJECT(WebRtcServer(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance,
+ StreamerSockets& sockets,
+ KernelLogPipeProvider& log_pipe_provider))
+ : config_(config),
+ instance_(instance),
+ sockets_(sockets),
+ log_pipe_provider_(log_pipe_provider) {}
+ // DiagnosticInformation
+ std::vector<std::string> Diagnostics() const override {
+ if (!Enabled() || !config_.ForDefaultInstance().start_webrtc_sig_server()) {
+ // When WebRTC is enabled but an operator other than the one launched by
+ // run_cvd is used there is no way to know the url to which to point the
+ // browser to.
+ return {};
+ }
+ std::ostringstream out;
+ out << "Point your browser to https://" << config_.sig_server_address()
+ << ":" << config_.sig_server_port() << " to interact with the device.";
+ return {out.str()};
+ }
+
+ // CommandSource
+ std::vector<Command> Commands() override {
+ std::vector<Command> commands;
+ if (instance_.start_webrtc_sig_server()) {
+ Command sig_server(WebRtcSigServerBinary());
+ sig_server.AddParameter("-assets_dir=", config_.webrtc_assets_dir());
+ sig_server.AddParameter(
+ "-use_secure_http=",
+ config_.sig_server_secure() ? "true" : "false");
+ if (!config_.webrtc_certs_dir().empty()) {
+ sig_server.AddParameter("-certs_dir=", config_.webrtc_certs_dir());
+ }
+ sig_server.AddParameter("-http_server_port=", config_.sig_server_port());
+ commands.emplace_back(std::move(sig_server));
+ }
+
+ auto stopper = [host_socket = std::move(host_socket_)](Subprocess* proc) {
+ struct timeval timeout;
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ CHECK(host_socket->SetSockOpt(SOL_SOCKET, SO_RCVTIMEO, &timeout,
+ sizeof(timeout)) == 0)
+ << "Could not set receive timeout";
+
+ WriteAll(host_socket, "C");
+ char response[1];
+ int read_ret = host_socket->Read(response, sizeof(response));
+ if (read_ret != 0) {
+ LOG(ERROR) << "Failed to read response from webrtc";
+ return KillSubprocess(proc);
+ }
+ return KillSubprocess(proc) == StopperResult::kStopSuccess
+ ? StopperResult::kStopCrash
+ : StopperResult::kStopFailure;
+ };
+
+ Command webrtc(WebRtcBinary(), stopper);
+ webrtc.UnsetFromEnvironment({"http_proxy"});
+ sockets_.AppendCommandArguments(webrtc);
+ if (config_.vm_manager() == vm_manager::CrosvmManager::name()) {
+ webrtc.AddParameter("-switches_fd=", switches_server_);
+ }
+ // Currently there is no way to ensure the signaling server will already
+ // have bound the socket to the port by the time the webrtc process runs
+ // (the common technique of doing it from the launcher is not possible here
+ // as the server library being used creates its own sockets). However, this
+ // issue is mitigated slightly by doing some retrying and backoff in the
+ // webrtc process when connecting to the websocket, so it shouldn't be an
+ // issue most of the time.
+ webrtc.AddParameter("--command_fd=", client_socket_);
+ webrtc.AddParameter("-kernel_log_events_fd=", kernel_log_events_pipe_);
+
+ // TODO get from launcher params
+ for (auto& action : LaunchCustomActionServers(webrtc, config_)) {
+ commands.emplace_back(std::move(action));
+ }
+ commands.emplace_back(std::move(webrtc));
+
+ return commands;
+ }
+
+ // Feature
+ bool Enabled() const override {
+ return sockets_.Enabled() && config_.enable_webrtc();
+ }
+ std::string Name() const override { return "WebRtcServer"; }
+ std::unordered_set<Feature*> Dependencies() const override {
+ return {static_cast<Feature*>(&sockets_),
+ static_cast<Feature*>(&log_pipe_provider_)};
+ }
+
+ protected:
+ bool Setup() override {
+ if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &client_socket_,
+ &host_socket_)) {
+ LOG(ERROR) << "Could not open command socket for webRTC";
+ return false;
+ }
+ if (config_.vm_manager() == vm_manager::CrosvmManager::name()) {
+ switches_server_ =
+ CreateUnixInputServer(instance_.switches_socket_path());
+ if (!switches_server_->IsOpen()) {
+ LOG(ERROR) << "Could not open switches server: "
+ << switches_server_->StrError();
+ return false;
+ }
+ }
+ kernel_log_events_pipe_ = log_pipe_provider_.KernelLogPipe();
+ if (!kernel_log_events_pipe_->IsOpen()) {
+ LOG(ERROR) << "Failed to get a kernel log events pipe: "
+ << kernel_log_events_pipe_->StrError();
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ StreamerSockets& sockets_;
+ KernelLogPipeProvider& log_pipe_provider_;
+ SharedFD kernel_log_events_pipe_;
+ SharedFD client_socket_;
+ SharedFD host_socket_;
+ SharedFD switches_server_;
+};
+
+} // namespace
+
+fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
+ const CuttlefishConfig::InstanceSpecific>>
+launchStreamerComponent() {
+ return fruit::createComponent()
+ .addMultibinding<CommandSource, WebRtcServer>()
+ .addMultibinding<CommandSource, VncServer>()
+ .addMultibinding<DiagnosticInformation, WebRtcServer>()
+ .addMultibinding<DiagnosticInformation, VncServer>()
+ .addMultibinding<Feature, StreamerSockets>()
+ .addMultibinding<Feature, WebRtcServer>()
+ .addMultibinding<Feature, VncServer>();
+}
+
+} // namespace cuttlefish
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index 13f840b..4c17b7d 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -14,375 +14,136 @@
* limitations under the License.
*/
-#include <limits.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/prctl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <fcntl.h>
#include <unistd.h>
-
-#include <algorithm>
-#include <functional>
-#include <iostream>
#include <fstream>
-#include <iomanip>
#include <memory>
-#include <sstream>
#include <string>
-#include <thread>
+#include <utility>
#include <vector>
#include <android-base/logging.h>
#include <android-base/strings.h>
+#include <fruit/fruit.h>
#include <gflags/gflags.h>
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
-#include "common/libs/fs/shared_select.h"
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
-#include "common/libs/utils/network.h"
#include "common/libs/utils/size_utils.h"
#include "common/libs/utils/subprocess.h"
#include "common/libs/utils/tee_logging.h"
#include "host/commands/run_cvd/boot_state_machine.h"
#include "host/commands/run_cvd/launch.h"
#include "host/commands/run_cvd/process_monitor.h"
+#include "host/commands/run_cvd/reporting.h"
#include "host/commands/run_cvd/runner_defs.h"
+#include "host/commands/run_cvd/server_loop.h"
+#include "host/commands/run_cvd/validate.h"
+#include "host/libs/config/adb_config.h"
+#include "host/libs/config/config_fragment.h"
#include "host/libs/config/cuttlefish_config.h"
-#include "host/libs/config/data_image.h"
-#include "host/libs/config/kernel_args.h"
-#include "host/libs/vm_manager/crosvm_manager.h"
-#include "host/libs/vm_manager/host_configuration.h"
-#include "host/libs/vm_manager/qemu_manager.h"
#include "host/libs/vm_manager/vm_manager.h"
-DEFINE_int32(reboot_notification_fd, -1,
- "A file descriptor to notify when boot completes.");
-
namespace cuttlefish {
using vm_manager::GetVmManager;
-using vm_manager::ValidateHostConfiguration;
namespace {
-constexpr char kGreenColor[] = "\033[1;32m";
-constexpr char kResetColor[] = "\033[0m";
+class CuttlefishEnvironment : public Feature, public DiagnosticInformation {
+ public:
+ INJECT(
+ CuttlefishEnvironment(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
-bool WriteCuttlefishEnvironment(const CuttlefishConfig& config) {
- auto env = SharedFD::Open(config.cuttlefish_env_path().c_str(),
- O_CREAT | O_RDWR, 0755);
- if (!env->IsOpen()) {
- LOG(ERROR) << "Unable to create cuttlefish.env file";
- return false;
+ // DiagnosticInformation
+ std::vector<std::string> Diagnostics() const override {
+ auto config_path = instance_.PerInstancePath("cuttlefish_config.json");
+ return {
+ "Launcher log: " + instance_.launcher_log_path(),
+ "Instance configuration: " + config_path,
+ "Instance environment: " + config_.cuttlefish_env_path(),
+ };
}
- auto instance = config.ForDefaultInstance();
- std::string config_env = "export CUTTLEFISH_PER_INSTANCE_PATH=\"" +
- instance.PerInstancePath(".") + "\"\n";
- config_env += "export ANDROID_SERIAL=" + instance.adb_ip_and_port() + "\n";
- env->Write(config_env.c_str(), config_env.size());
- return true;
+
+ // Feature
+ bool Enabled() const override { return true; }
+ std::string Name() const override { return "CuttlefishEnvironment"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ auto env =
+ SharedFD::Open(config_.cuttlefish_env_path(), O_CREAT | O_RDWR, 0755);
+ if (!env->IsOpen()) {
+ LOG(ERROR) << "Unable to create cuttlefish.env file";
+ return false;
+ }
+ std::string config_env = "export CUTTLEFISH_PER_INSTANCE_PATH=\"" +
+ instance_.PerInstancePath(".") + "\"\n";
+ config_env += "export ANDROID_SERIAL=" + instance_.adb_ip_and_port() + "\n";
+ auto written = WriteAll(env, config_env);
+ if (written != config_env.size()) {
+ LOG(ERROR) << "Failed to write all of \"" << config_env << "\", "
+ << "only wrote " << written << " bytes. Error was "
+ << env->StrError();
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+};
+
+fruit::Component<ServerLoop> runCvdComponent(
+ const CuttlefishConfig* config,
+ const CuttlefishConfig::InstanceSpecific* instance) {
+ return fruit::createComponent()
+ .addMultibinding<DiagnosticInformation, CuttlefishEnvironment>()
+ .addMultibinding<Feature, CuttlefishEnvironment>()
+ .bindInstance(*config)
+ .bindInstance(*instance)
+ .install(AdbConfigComponent)
+ .install(bootStateMachineComponent)
+ .install(launchAdbComponent)
+ .install(launchComponent)
+ .install(launchModemComponent)
+ .install(launchStreamerComponent)
+ .install(serverLoopComponent)
+ .install(validationComponent);
}
-// Forks and returns the write end of a pipe to the child process. The parent
-// process waits for boot events to come through the pipe and exits accordingly.
-SharedFD DaemonizeLauncher(const CuttlefishConfig& config) {
- auto instance = config.ForDefaultInstance();
- SharedFD read_end, write_end;
- if (!SharedFD::Pipe(&read_end, &write_end)) {
- LOG(ERROR) << "Unable to create pipe";
- return {}; // a closed FD
- }
- auto pid = fork();
- if (pid) {
- // Explicitly close here, otherwise we may end up reading forever if the
- // child process dies.
- write_end->Close();
- RunnerExitCodes 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 = RunnerExitCodes::kPipeIOError;
- } else if (exit_code == RunnerExitCodes::kSuccess) {
- LOG(INFO) << "Virtual device booted successfully";
- } else if (exit_code == RunnerExitCodes::kVirtualDeviceBootFailed) {
- LOG(ERROR) << "Virtual device failed to boot";
- } else {
- LOG(ERROR) << "Unexpected exit code: " << exit_code;
- }
- if (exit_code == RunnerExitCodes::kSuccess) {
- LOG(INFO) << kBootCompletedMessage;
- } else {
- LOG(INFO) << kBootFailedMessage;
- }
- std::exit(exit_code);
- } else {
- // The child returns the write end of the pipe
- if (daemon(/*nochdir*/ 1, /*noclose*/ 1) != 0) {
- LOG(ERROR) << "Failed to daemonize child process: " << strerror(errno);
- std::exit(RunnerExitCodes::kDaemonizationError);
- }
- // Redirect standard I/O
- auto log_path = instance.launcher_log_path();
- auto log = SharedFD::Open(log_path.c_str(), O_CREAT | O_WRONLY | O_APPEND,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
- if (!log->IsOpen()) {
- LOG(ERROR) << "Failed to create launcher log file: " << log->StrError();
- std::exit(RunnerExitCodes::kDaemonizationError);
- }
- ::android::base::SetLogger(
- TeeLogger({{LogFileSeverity(), log, MetadataLevel::FULL}}));
- auto dev_null = SharedFD::Open("/dev/null", O_RDONLY);
- if (!dev_null->IsOpen()) {
- LOG(ERROR) << "Failed to open /dev/null: " << dev_null->StrError();
- std::exit(RunnerExitCodes::kDaemonizationError);
- }
- if (dev_null->UNMANAGED_Dup2(0) < 0) {
- LOG(ERROR) << "Failed dup2 stdin: " << dev_null->StrError();
- std::exit(RunnerExitCodes::kDaemonizationError);
- }
- if (log->UNMANAGED_Dup2(1) < 0) {
- LOG(ERROR) << "Failed dup2 stdout: " << log->StrError();
- std::exit(RunnerExitCodes::kDaemonizationError);
- }
- if (log->UNMANAGED_Dup2(2) < 0) {
- LOG(ERROR) << "Failed dup2 seterr: " << log->StrError();
- std::exit(RunnerExitCodes::kDaemonizationError);
- }
-
- read_end->Close();
- return write_end;
- }
-}
-
-bool CreateQcowOverlay(const std::string& crosvm_path,
- const std::string& backing_file,
- const std::string& output_overlay_path) {
- Command crosvm_qcow2_cmd(crosvm_path);
- crosvm_qcow2_cmd.AddParameter("create_qcow2");
- crosvm_qcow2_cmd.AddParameter("--backing_file=", backing_file);
- crosvm_qcow2_cmd.AddParameter(output_overlay_path);
- int success = crosvm_qcow2_cmd.Start().Wait();
- if (success != 0) {
- LOG(ERROR) << "Unable to run crosvm create_qcow2. Exited with status " << success;
- return false;
- }
- return true;
-}
-
-void DeleteFifos(const CuttlefishConfig::InstanceSpecific& instance) {
- // TODO(schuffelen): Create these FIFOs in assemble_cvd instead of run_cvd.
- std::vector<std::string> pipes = {
- instance.kernel_log_pipe_name(),
- instance.console_in_pipe_name(),
- instance.console_out_pipe_name(),
- instance.logcat_pipe_name(),
- instance.PerInstanceInternalPath("keymaster_fifo_vm.in"),
- instance.PerInstanceInternalPath("keymaster_fifo_vm.out"),
- instance.PerInstanceInternalPath("gatekeeper_fifo_vm.in"),
- instance.PerInstanceInternalPath("gatekeeper_fifo_vm.out"),
- instance.PerInstanceInternalPath("bt_fifo_vm.in"),
- instance.PerInstanceInternalPath("bt_fifo_vm.out"),
- };
- for (const auto& pipe : pipes) {
- unlink(pipe.c_str());
- }
-}
-
-bool PowerwashFiles() {
- auto config = CuttlefishConfig::Get();
- if (!config) {
- LOG(ERROR) << "Could not load the config.";
- return false;
- }
- auto instance = config->ForDefaultInstance();
-
- DeleteFifos(instance);
-
- // TODO(schuffelen): Clean up duplication with assemble_cvd
- auto kregistry_path = instance.access_kregistry_path();
- unlink(kregistry_path.c_str());
- CreateBlankImage(kregistry_path, 2 /* mb */, "none");
-
- auto pstore_path = instance.pstore_path();
- unlink(pstore_path.c_str());
- CreateBlankImage(pstore_path, 2 /* mb */, "none");
-
- auto sdcard_path = instance.sdcard_path();
- auto sdcard_size = FileSize(sdcard_path);
- unlink(sdcard_path.c_str());
- // round up
- auto sdcard_mb_size = (sdcard_size + (1 << 20) - 1) / (1 << 20);
- LOG(DEBUG) << "Size in mb is " << sdcard_mb_size;
- CreateBlankImage(sdcard_path, sdcard_mb_size, "sdcard");
-
- auto overlay_path = instance.PerInstancePath("overlay.img");
- unlink(overlay_path.c_str());
- if (!CreateQcowOverlay(config->crosvm_binary(),
- instance.os_composite_disk_path(), overlay_path)) {
- LOG(ERROR) << "CreateQcowOverlay failed";
- return false;
- }
- return true;
-}
-
-void RestartRunCvd(const CuttlefishConfig& config, int notification_fd) {
- auto config_path = config.AssemblyPath("cuttlefish_config.json");
- auto followup_stdin = SharedFD::MemfdCreate("pseudo_stdin");
- WriteAll(followup_stdin, config_path + "\n");
- followup_stdin->LSeek(0, SEEK_SET);
- followup_stdin->UNMANAGED_Dup2(0);
-
- auto argv_vec = gflags::GetArgvs();
- char** argv = new char*[argv_vec.size() + 2];
- for (size_t i = 0; i < argv_vec.size(); i++) {
- argv[i] = argv_vec[i].data();
- }
- // Will take precedence over any earlier arguments.
- std::string reboot_notification =
- "-reboot_notification_fd=" + std::to_string(notification_fd);
- argv[argv_vec.size()] = reboot_notification.data();
- argv[argv_vec.size() + 1] = nullptr;
-
- execv("/proc/self/exe", argv);
- // execve should not return, so something went wrong.
- PLOG(ERROR) << "execv returned: ";
-}
-
-void ServerLoop(SharedFD server, ProcessMonitor* process_monitor) {
- while (true) {
- // TODO: use select to handle simultaneous connections.
- auto client = SharedFD::Accept(*server);
- LauncherAction action;
- while (client->IsOpen() && client->Read(&action, sizeof(action)) > 0) {
- switch (action) {
- case LauncherAction::kStop:
- if (process_monitor->StopMonitoredProcesses()) {
- auto response = LauncherResponse::kSuccess;
- client->Write(&response, sizeof(response));
- std::exit(0);
- } else {
- auto response = LauncherResponse::kError;
- client->Write(&response, sizeof(response));
- }
- break;
- case LauncherAction::kStatus: {
- // TODO(schuffelen): Return more information on a side channel
- auto response = LauncherResponse::kSuccess;
- client->Write(&response, sizeof(response));
- break;
- }
- case LauncherAction::kPowerwash: {
- LOG(INFO) << "Received a Powerwash request from the monitor socket";
- if (!process_monitor->StopMonitoredProcesses()) {
- LOG(ERROR) << "Stopping processes failed.";
- auto response = LauncherResponse::kError;
- client->Write(&response, sizeof(response));
- break;
- }
- if (!PowerwashFiles()) {
- LOG(ERROR) << "Powerwashing files failed.";
- auto response = LauncherResponse::kError;
- client->Write(&response, sizeof(response));
- break;
- }
- auto response = LauncherResponse::kSuccess;
- client->Write(&response, sizeof(response));
-
- auto config = CuttlefishConfig::Get();
- CHECK(config) << "Could not load config";
- RestartRunCvd(*config, client->UNMANAGED_Dup());
- // RestartRunCvd should not return, so something went wrong.
- response = LauncherResponse::kError;
- client->Write(&response, sizeof(response));
- LOG(FATAL) << "run_cvd in a bad state";
- break;
- }
- case LauncherAction::kRestart: {
- if (!process_monitor->StopMonitoredProcesses()) {
- LOG(ERROR) << "Stopping processes failed.";
- auto response = LauncherResponse::kError;
- client->Write(&response, sizeof(response));
- break;
- }
-
- auto config = CuttlefishConfig::Get();
- CHECK(config) << "Could not load config";
- auto instance = config->ForDefaultInstance();
- DeleteFifos(instance);
-
- auto response = LauncherResponse::kSuccess;
- client->Write(&response, sizeof(response));
- CHECK(config) << "Could not load config";
- RestartRunCvd(*config, client->UNMANAGED_Dup());
- // RestartRunCvd should not return, so something went wrong.
- response = LauncherResponse::kError;
- client->Write(&response, sizeof(response));
- LOG(FATAL) << "run_cvd in a bad state";
- break;
- }
- default:
- LOG(ERROR) << "Unrecognized launcher action: "
- << static_cast<char>(action);
- auto response = LauncherResponse::kError;
- client->Write(&response, sizeof(response));
- }
- }
- }
-}
-
-std::string GetConfigFilePath(const CuttlefishConfig& config) {
- auto instance = config.ForDefaultInstance();
- return instance.PerInstancePath("cuttlefish_config.json");
-}
-
-void PrintStreamingInformation(const CuttlefishConfig& config) {
- if (config.ForDefaultInstance().start_webrtc_sig_server()) {
- // TODO (jemoreira): Change this when webrtc is moved to the debian package.
- LOG(INFO) << kGreenColor << "Point your browser to https://"
- << config.sig_server_address() << ":" << config.sig_server_port()
- << " to interact with the device." << kResetColor;
- } else if (config.enable_vnc_server()) {
- LOG(INFO) << kGreenColor << "VNC server started on port "
- << config.ForDefaultInstance().vnc_server_port() << kResetColor;
- }
- // When WebRTC is enabled but an operator other than the one launched by
- // run_cvd is used there is no way to know the url to which to point the
- // browser to.
-}
-
-} // namespace
-
-int RunCvdMain(int argc, char** argv) {
- setenv("ANDROID_LOG_TAGS", "*:v", /* overwrite */ 0);
- ::android::base::InitLogging(argv, android::base::StderrLogger);
- google::ParseCommandLineFlags(&argc, &argv, false);
-
+bool IsStdinValid() {
if (isatty(0)) {
- LOG(FATAL) << "stdin was a tty, expected to be passed the output of a previous stage. "
+ LOG(ERROR) << "stdin was a tty, expected to be passed the output of a "
+ "previous stage. "
<< "Did you mean to run launch_cvd?";
- return RunnerExitCodes::kInvalidHostConfiguration;
+ return false;
} else {
int error_num = errno;
if (error_num == EBADF) {
- LOG(FATAL) << "stdin was not a valid file descriptor, expected to be passed the output "
+ LOG(ERROR) << "stdin was not a valid file descriptor, expected to be "
+ "passed the output "
<< "of assemble_cvd. Did you mean to run launch_cvd?";
- return RunnerExitCodes::kInvalidHostConfiguration;
+ return false;
}
}
+ return true;
+}
+const CuttlefishConfig* FindConfigFromStdin() {
std::string input_files_str;
{
auto input_fd = SharedFD::Dup(0);
auto bytes_read = ReadAll(input_fd, &input_files_str);
if (bytes_read < 0) {
- LOG(FATAL) << "Failed to read input files. Error was \"" << input_fd->StrError() << "\"";
+ LOG(ERROR) << "Failed to read input files. Error was \""
+ << input_fd->StrError() << "\"";
+ return nullptr;
}
}
std::vector<std::string> input_files = android::base::Split(input_files_str, "\n");
@@ -393,186 +154,85 @@
setenv(kCuttlefishConfigEnvVarName, file.c_str(), /* overwrite */ false);
}
}
- if (!found_config) {
- return RunnerExitCodes::kCuttlefishConfigurationInitError;
- }
+ return CuttlefishConfig::Get();
+}
- auto config = CuttlefishConfig::Get();
- auto instance = config->ForDefaultInstance();
-
+void ConfigureLogs(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance) {
auto log_path = instance.launcher_log_path();
- {
- std::ofstream launcher_log_ofstream(log_path.c_str());
- auto assembly_path = config->AssemblyPath("assemble_cvd.log");
- std::ifstream assembly_log_ifstream(assembly_path);
- if (assembly_log_ifstream) {
- auto assemble_log = ReadFile(assembly_path);
- launcher_log_ofstream << assemble_log;
- }
+ std::ofstream launcher_log_ofstream(log_path.c_str());
+ auto assembly_path = config.AssemblyPath("assemble_cvd.log");
+ std::ifstream assembly_log_ifstream(assembly_path);
+ if (assembly_log_ifstream) {
+ auto assemble_log = ReadFile(assembly_path);
+ launcher_log_ofstream << assemble_log;
}
::android::base::SetLogger(LogToStderrAndFiles({log_path}));
+}
+bool ChdirIntoRuntimeDir(const CuttlefishConfig::InstanceSpecific& instance) {
// Change working directory to the instance directory as early as possible to
// ensure all host processes have the same working dir. This helps stop_cvd
// find the running processes when it can't establish a communication with the
// launcher.
auto chdir_ret = chdir(instance.instance_dir().c_str());
if (chdir_ret != 0) {
- auto error = errno;
- LOG(ERROR) << "Unable to change dir into instance directory ("
- << instance.instance_dir() << "): " << strerror(error);
- return RunnerExitCodes::kInstanceDirCreationError;
+ PLOG(ERROR) << "Unable to change dir into instance directory ("
+ << instance.instance_dir() << "): ";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+int RunCvdMain(int argc, char** argv) {
+ setenv("ANDROID_LOG_TAGS", "*:v", /* overwrite */ 0);
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ google::ParseCommandLineFlags(&argc, &argv, false);
+
+ CHECK(IsStdinValid()) << "Invalid stdin";
+ auto config = FindConfigFromStdin();
+ CHECK(config) << "Could not find config";
+ auto instance = config->ForDefaultInstance();
+
+ ConfigureLogs(*config, instance);
+ CHECK(ChdirIntoRuntimeDir(instance)) << "Could not enter runtime dir";
+
+ fruit::Injector<ServerLoop> injector(runCvdComponent, config, &instance);
+
+ for (auto& fragment : injector.getMultibindings<ConfigFragment>()) {
+ CHECK(config->LoadFragment(*fragment)) << "Failed to load config fragment";
}
- auto used_tap_devices = TapInterfacesInUse();
- if (used_tap_devices.count(instance.wifi_tap_name())) {
- LOG(ERROR) << "Wifi TAP device already in use";
- return RunnerExitCodes::kTapDeviceInUse;
- } else if (used_tap_devices.count(instance.mobile_tap_name())) {
- LOG(ERROR) << "Mobile TAP device already in use";
- return RunnerExitCodes::kTapDeviceInUse;
- } else if (config->ethernet() &&
- used_tap_devices.count(instance.ethernet_tap_name())) {
- LOG(ERROR) << "Ethernet TAP device already in use";
- }
+ // One of the setup features can consume most output, so print this early.
+ DiagnosticInformation::PrintAll(
+ injector.getMultibindings<DiagnosticInformation>());
- auto vm_manager = GetVmManager(config->vm_manager(), config->target_arch());
-
-#ifndef __ANDROID__
- // Check host configuration
- std::vector<std::string> config_commands;
- if (!ValidateHostConfiguration(&config_commands)) {
- LOG(ERROR) << "Validation of user configuration failed";
- std::cout << "Execute the following to correctly configure:" << std::endl;
- for (auto& command : config_commands) {
- std::cout << " " << command << std::endl;
- }
- std::cout << "You may need to logout for the changes to take effect"
- << std::endl;
- return RunnerExitCodes::kInvalidHostConfiguration;
- }
-#endif
-
- if (!WriteCuttlefishEnvironment(*config)) {
- LOG(ERROR) << "Unable to write cuttlefish environment file";
- }
-
- PrintStreamingInformation(*config);
-
- if (config->console()) {
- LOG(INFO) << kGreenColor << "To access the console run: screen "
- << instance.console_path() << kResetColor;
- } else {
- LOG(INFO) << kGreenColor
- << "Serial console is disabled; use -console=true to enable it"
- << kResetColor;
- }
-
- LOG(INFO) << kGreenColor
- << "The following files contain useful debugging information:"
- << kResetColor;
- LOG(INFO) << kGreenColor
- << " Launcher log: " << instance.launcher_log_path()
- << kResetColor;
- LOG(INFO) << kGreenColor
- << " Android's logcat output: " << instance.logcat_path()
- << kResetColor;
- LOG(INFO) << kGreenColor
- << " Kernel log: " << instance.PerInstancePath("kernel.log")
- << kResetColor;
- LOG(INFO) << kGreenColor
- << " Instance configuration: " << GetConfigFilePath(*config)
- << kResetColor;
- LOG(INFO) << kGreenColor
- << " Instance environment: " << config->cuttlefish_env_path()
- << kResetColor;
-
- auto launcher_monitor_path = instance.launcher_monitor_socket_path();
- auto launcher_monitor_socket = SharedFD::SocketLocalServer(
- launcher_monitor_path.c_str(), false, SOCK_STREAM, 0666);
- if (!launcher_monitor_socket->IsOpen()) {
- LOG(ERROR) << "Error when opening launcher server: "
- << launcher_monitor_socket->StrError();
- return RunnerExitCodes::kMonitorCreationFailed;
- }
- SharedFD foreground_launcher_pipe;
- if (config->run_as_daemon()) {
- foreground_launcher_pipe = DaemonizeLauncher(*config);
- if (!foreground_launcher_pipe->IsOpen()) {
- return RunnerExitCodes::kDaemonizationError;
- }
- } else {
- // Make sure the launcher runs in its own process group even when running in
- // foreground
- if (getsid(0) != getpid()) {
- int retval = setpgid(0, 0);
- if (retval) {
- LOG(ERROR) << "Failed to create new process group: " << strerror(errno);
- std::exit(RunnerExitCodes::kProcessGroupError);
- }
- }
- }
-
- SharedFD reboot_notification;
- if (FLAGS_reboot_notification_fd >= 0) {
- reboot_notification = SharedFD::Dup(FLAGS_reboot_notification_fd);
- close(FLAGS_reboot_notification_fd);
- }
+ const auto& features = injector.getMultibindings<Feature>();
+ CHECK(Feature::RunSetup(features)) << "Failed to run feature setup.";
// Monitor and restart host processes supporting the CVD
ProcessMonitor process_monitor(config->restart_subprocesses());
- if (config->enable_metrics() == CuttlefishConfig::kYes) {
- LaunchMetrics(&process_monitor);
+ for (auto& command_source : injector.getMultibindings<CommandSource>()) {
+ if (command_source->Enabled()) {
+ process_monitor.AddCommands(command_source->Commands());
+ }
}
- LaunchModemSimulatorIfEnabled(*config, &process_monitor);
-
- auto event_pipes =
- LaunchKernelLogMonitor(*config, &process_monitor, 3);
- SharedFD boot_events_pipe = event_pipes[0];
- SharedFD adbd_events_pipe = event_pipes[1];
- SharedFD webrtc_events_pipe = event_pipes[2];
- event_pipes.clear();
-
- CvdBootStateMachine boot_state_machine(foreground_launcher_pipe,
- reboot_notification, boot_events_pipe);
-
- LaunchRootCanal(*config, &process_monitor);
- LaunchLogcatReceiver(*config, &process_monitor);
- LaunchConfigServer(*config, &process_monitor);
- LaunchTombstoneReceiver(*config, &process_monitor);
- LaunchGnssGrpcProxyServerIfEnabled(*config, &process_monitor);
- LaunchSecureEnvironment(&process_monitor, *config);
- if (config->enable_host_bluetooth()) {
- LaunchBluetoothConnector(&process_monitor, *config);
- }
- LaunchVehicleHalServerIfEnabled(*config, &process_monitor);
- LaunchConsoleForwarderIfEnabled(*config, &process_monitor);
// The streamer needs to launch before the VMM because it serves on several
// sockets (input devices, vsock frame server) when using crosvm.
- if (config->enable_vnc_server()) {
- LaunchVNCServer(*config, &process_monitor);
- }
- if (config->enable_webrtc()) {
- LaunchWebRTC(&process_monitor, *config, webrtc_events_pipe);
- }
// Start the guest VM
- auto vmm_commands = vm_manager->StartCommands(*config);
- for (auto& vmm_cmd: vmm_commands) {
- process_monitor.AddCommand(std::move(vmm_cmd));
- }
-
- // Start other host processes
- LaunchSocketVsockProxyIfEnabled(&process_monitor, *config, adbd_events_pipe);
- LaunchAdbConnectorIfEnabled(&process_monitor, *config);
+ auto vm_manager = GetVmManager(config->vm_manager(), config->target_arch());
+ process_monitor.AddCommands(vm_manager->StartCommands(*config));
CHECK(process_monitor.StartAndMonitorProcesses())
<< "Could not start subprocesses";
- ServerLoop(launcher_monitor_socket, &process_monitor); // Should not return
+ injector.get<ServerLoop&>().Run(process_monitor); // Should not return
LOG(ERROR) << "The server loop returned, it should never happen!!";
return RunnerExitCodes::kServerError;
diff --git a/host/commands/run_cvd/process_monitor.cc b/host/commands/run_cvd/process_monitor.cc
index a4f7f72..53fc8b6 100644
--- a/host/commands/run_cvd/process_monitor.cc
+++ b/host/commands/run_cvd/process_monitor.cc
@@ -118,7 +118,7 @@
}
static void LogSubprocessExit(const std::string& name, pid_t pid, int wstatus) {
- LOG(INFO) << "Detected exit of monitored subprocess " << name;
+ LOG(INFO) << "Detected unexpected exit of monitored subprocess " << name;
if (WIFEXITED(wstatus)) {
LOG(INFO) << "Subprocess " << name << " (" << pid
<< ") has exited with exit code " << WEXITSTATUS(wstatus);
@@ -195,21 +195,25 @@
}
parent_comms_thread.join(); // Should have exited if `running` is false
- // Processes were started in the order they appear in the vector, stop them in
- // reverse order for symmetry.
auto stop = [](const auto& it) {
- if (!it.proc->Stop()) {
+ auto stop_result = it.proc->Stop();
+ if (stop_result == StopperResult::kStopFailure) {
LOG(WARNING) << "Error in stopping \"" << it.cmd->GetShortName() << "\"";
return false;
}
int wstatus = 0;
- auto ret = it.proc->Wait(&wstatus, 0);
- if (ret < 0) {
+ auto pid = it.proc->Wait(&wstatus, 0);
+ if (pid < 0) {
LOG(WARNING) << "Failed to wait for process " << it.cmd->GetShortName();
return false;
}
+ if (stop_result == StopperResult::kStopCrash) {
+ LogSubprocessExit(it.cmd->GetShortName(), pid, wstatus);
+ }
return true;
};
+ // Processes were started in the order they appear in the vector, stop them in
+ // reverse order for symmetry.
size_t stopped = std::count_if(monitored.rbegin(), monitored.rend(), stop);
LOG(DEBUG) << "Done monitoring subprocesses";
return stopped == monitored.size();
diff --git a/host/commands/run_cvd/process_monitor.h b/host/commands/run_cvd/process_monitor.h
index 5d00096..18b8ab8 100644
--- a/host/commands/run_cvd/process_monitor.h
+++ b/host/commands/run_cvd/process_monitor.h
@@ -42,6 +42,12 @@
// called before StartAndMonitorProcesses is called. OnSocketReadyCb will be
// called inside a forked process.
void AddCommand(Command cmd);
+ template <typename T>
+ void AddCommands(T&& commands) {
+ for (auto& command : commands) {
+ AddCommand(std::move(command));
+ }
+ }
// Start all processes given by AddCommand.
bool StartAndMonitorProcesses();
diff --git a/host/commands/run_cvd/reporting.cpp b/host/commands/run_cvd/reporting.cpp
new file mode 100644
index 0000000..db25185
--- /dev/null
+++ b/host/commands/run_cvd/reporting.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/commands/run_cvd/reporting.h"
+
+#include <android-base/logging.h>
+#include <fruit/fruit.h>
+#include <string>
+#include <vector>
+
+namespace cuttlefish {
+
+static constexpr char kGreenColor[] = "\033[1;32m";
+static constexpr char kResetColor[] = "\033[0m";
+
+DiagnosticInformation::~DiagnosticInformation() = default;
+
+void DiagnosticInformation::PrintAll(
+ const std::vector<DiagnosticInformation*>& infos) {
+ LOG(INFO) << kGreenColor
+ << "The following files contain useful debugging information:"
+ << kResetColor;
+ for (const auto& info : infos) {
+ for (const auto& line : info->Diagnostics()) {
+ LOG(INFO) << kGreenColor << " " << line << kResetColor;
+ }
+ }
+}
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/size_utils.cpp b/host/commands/run_cvd/reporting.h
similarity index 64%
copy from common/libs/utils/size_utils.cpp
copy to host/commands/run_cvd/reporting.h
index 9f25445..f6b4d5a 100644
--- a/common/libs/utils/size_utils.cpp
+++ b/host/commands/run_cvd/reporting.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 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,15 +14,20 @@
* limitations under the License.
*/
-#include "common/libs/utils/size_utils.h"
+#pragma once
-#include <unistd.h>
+#include <fruit/fruit.h>
+#include <string>
+#include <vector>
namespace cuttlefish {
-uint64_t AlignToPowerOf2(uint64_t val, uint8_t align_log) {
- uint64_t align = 1ULL << align_log;
- return ((val + (align - 1)) / align) * align;
-}
+class DiagnosticInformation {
+ public:
+ virtual ~DiagnosticInformation();
+ virtual std::vector<std::string> Diagnostics() const = 0;
+
+ static void PrintAll(const std::vector<DiagnosticInformation*>&);
+};
} // namespace cuttlefish
diff --git a/host/commands/run_cvd/runner_defs.h b/host/commands/run_cvd/runner_defs.h
index e27597a..b51e894 100644
--- a/host/commands/run_cvd/runner_defs.h
+++ b/host/commands/run_cvd/runner_defs.h
@@ -44,6 +44,7 @@
kTapDeviceInUse = 23,
kTpmPassthroughError = 24,
kModemSimulatorServerError = 25,
+ kSocketProxyServerError = 26,
};
// Actions supported by the launcher server
diff --git a/host/commands/run_cvd/server_loop.cpp b/host/commands/run_cvd/server_loop.cpp
new file mode 100644
index 0000000..2b69aef
--- /dev/null
+++ b/host/commands/run_cvd/server_loop.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/commands/run_cvd/server_loop.h"
+
+#include <fruit/fruit.h>
+#include <gflags/gflags.h>
+#include <unistd.h>
+#include <string>
+
+#include "common/libs/fs/shared_buf.h"
+#include "common/libs/utils/files.h"
+#include "common/libs/utils/subprocess.h"
+#include "host/commands/run_cvd/runner_defs.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/data_image.h"
+#include "host/libs/config/feature.h"
+
+namespace cuttlefish {
+
+namespace {
+
+bool CreateQcowOverlay(const std::string& crosvm_path,
+ const std::string& backing_file,
+ const std::string& output_overlay_path) {
+ Command crosvm_qcow2_cmd(crosvm_path);
+ crosvm_qcow2_cmd.AddParameter("create_qcow2");
+ crosvm_qcow2_cmd.AddParameter("--backing_file=", backing_file);
+ crosvm_qcow2_cmd.AddParameter(output_overlay_path);
+ int success = crosvm_qcow2_cmd.Start().Wait();
+ if (success != 0) {
+ LOG(ERROR) << "Unable to run crosvm create_qcow2. Exited with status "
+ << success;
+ return false;
+ }
+ return true;
+}
+
+class ServerLoopImpl : public ServerLoop, public Feature {
+ public:
+ INJECT(ServerLoopImpl(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+
+ // ServerLoop
+ void Run(ProcessMonitor& process_monitor) override {
+ while (true) {
+ // TODO: use select to handle simultaneous connections.
+ auto client = SharedFD::Accept(*server_);
+ LauncherAction action;
+ while (client->IsOpen() && client->Read(&action, sizeof(action)) > 0) {
+ switch (action) {
+ case LauncherAction::kStop:
+ if (process_monitor.StopMonitoredProcesses()) {
+ auto response = LauncherResponse::kSuccess;
+ client->Write(&response, sizeof(response));
+ std::exit(0);
+ } else {
+ auto response = LauncherResponse::kError;
+ client->Write(&response, sizeof(response));
+ }
+ break;
+ case LauncherAction::kStatus: {
+ // TODO(schuffelen): Return more information on a side channel
+ auto response = LauncherResponse::kSuccess;
+ client->Write(&response, sizeof(response));
+ break;
+ }
+ case LauncherAction::kPowerwash: {
+ LOG(INFO) << "Received a Powerwash request from the monitor socket";
+ if (!process_monitor.StopMonitoredProcesses()) {
+ LOG(ERROR) << "Stopping processes failed.";
+ auto response = LauncherResponse::kError;
+ client->Write(&response, sizeof(response));
+ break;
+ }
+ if (!PowerwashFiles()) {
+ LOG(ERROR) << "Powerwashing files failed.";
+ auto response = LauncherResponse::kError;
+ client->Write(&response, sizeof(response));
+ break;
+ }
+ auto response = LauncherResponse::kSuccess;
+ client->Write(&response, sizeof(response));
+
+ RestartRunCvd(client->UNMANAGED_Dup());
+ // RestartRunCvd should not return, so something went wrong.
+ response = LauncherResponse::kError;
+ client->Write(&response, sizeof(response));
+ LOG(FATAL) << "run_cvd in a bad state";
+ break;
+ }
+ case LauncherAction::kRestart: {
+ if (!process_monitor.StopMonitoredProcesses()) {
+ LOG(ERROR) << "Stopping processes failed.";
+ auto response = LauncherResponse::kError;
+ client->Write(&response, sizeof(response));
+ break;
+ }
+ DeleteFifos();
+
+ auto response = LauncherResponse::kSuccess;
+ client->Write(&response, sizeof(response));
+ RestartRunCvd(client->UNMANAGED_Dup());
+ // RestartRunCvd should not return, so something went wrong.
+ response = LauncherResponse::kError;
+ client->Write(&response, sizeof(response));
+ LOG(FATAL) << "run_cvd in a bad state";
+ break;
+ }
+ default:
+ LOG(ERROR) << "Unrecognized launcher action: "
+ << static_cast<char>(action);
+ auto response = LauncherResponse::kError;
+ client->Write(&response, sizeof(response));
+ }
+ }
+ }
+ }
+
+ // Feature
+ bool Enabled() const override { return true; }
+ std::string Name() const override { return "ServerLoop"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() {
+ auto launcher_monitor_path = instance_.launcher_monitor_socket_path();
+ server_ = SharedFD::SocketLocalServer(launcher_monitor_path.c_str(), false,
+ SOCK_STREAM, 0666);
+ if (!server_->IsOpen()) {
+ LOG(ERROR) << "Error when opening launcher server: "
+ << server_->StrError();
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ void DeleteFifos() {
+ // TODO(schuffelen): Create these FIFOs in assemble_cvd instead of run_cvd.
+ std::vector<std::string> pipes = {
+ instance_.kernel_log_pipe_name(),
+ instance_.console_in_pipe_name(),
+ instance_.console_out_pipe_name(),
+ instance_.logcat_pipe_name(),
+ instance_.PerInstanceInternalPath("keymaster_fifo_vm.in"),
+ instance_.PerInstanceInternalPath("keymaster_fifo_vm.out"),
+ instance_.PerInstanceInternalPath("gatekeeper_fifo_vm.in"),
+ instance_.PerInstanceInternalPath("gatekeeper_fifo_vm.out"),
+ instance_.PerInstanceInternalPath("bt_fifo_vm.in"),
+ instance_.PerInstanceInternalPath("bt_fifo_vm.out"),
+ };
+ for (const auto& pipe : pipes) {
+ unlink(pipe.c_str());
+ }
+ }
+
+ bool PowerwashFiles() {
+ DeleteFifos();
+
+ // TODO(schuffelen): Clean up duplication with assemble_cvd
+ auto kregistry_path = instance_.access_kregistry_path();
+ unlink(kregistry_path.c_str());
+ CreateBlankImage(kregistry_path, 2 /* mb */, "none");
+
+ auto pstore_path = instance_.pstore_path();
+ unlink(pstore_path.c_str());
+ CreateBlankImage(pstore_path, 2 /* mb */, "none");
+
+ auto sdcard_path = instance_.sdcard_path();
+ auto sdcard_size = FileSize(sdcard_path);
+ unlink(sdcard_path.c_str());
+ // round up
+ auto sdcard_mb_size = (sdcard_size + (1 << 20) - 1) / (1 << 20);
+ LOG(DEBUG) << "Size in mb is " << sdcard_mb_size;
+ CreateBlankImage(sdcard_path, sdcard_mb_size, "sdcard");
+
+ auto overlay_path = instance_.PerInstancePath("overlay.img");
+ unlink(overlay_path.c_str());
+ if (!CreateQcowOverlay(config_.crosvm_binary(),
+ config_.os_composite_disk_path(), overlay_path)) {
+ LOG(ERROR) << "CreateQcowOverlay failed";
+ return false;
+ }
+ return true;
+ }
+
+ void RestartRunCvd(int notification_fd) {
+ auto config_path = config_.AssemblyPath("cuttlefish_config.json");
+ auto followup_stdin = SharedFD::MemfdCreate("pseudo_stdin");
+ WriteAll(followup_stdin, config_path + "\n");
+ followup_stdin->LSeek(0, SEEK_SET);
+ followup_stdin->UNMANAGED_Dup2(0);
+
+ auto argv_vec = gflags::GetArgvs();
+ char** argv = new char*[argv_vec.size() + 2];
+ for (size_t i = 0; i < argv_vec.size(); i++) {
+ argv[i] = argv_vec[i].data();
+ }
+ // Will take precedence over any earlier arguments.
+ std::string reboot_notification =
+ "-reboot_notification_fd=" + std::to_string(notification_fd);
+ argv[argv_vec.size()] = reboot_notification.data();
+ argv[argv_vec.size() + 1] = nullptr;
+
+ execv("/proc/self/exe", argv);
+ // execve should not return, so something went wrong.
+ PLOG(ERROR) << "execv returned: ";
+ }
+
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+ SharedFD server_;
+};
+
+} // namespace
+
+ServerLoop::~ServerLoop() = default;
+
+fruit::Component<fruit::Required<const CuttlefishConfig,
+ const CuttlefishConfig::InstanceSpecific>,
+ ServerLoop>
+serverLoopComponent() {
+ return fruit::createComponent()
+ .bind<ServerLoop, ServerLoopImpl>()
+ .addMultibinding<Feature, ServerLoopImpl>();
+}
+
+} // namespace cuttlefish
diff --git a/host/commands/run_cvd/server_loop.h b/host/commands/run_cvd/server_loop.h
new file mode 100644
index 0000000..2364cb9
--- /dev/null
+++ b/host/commands/run_cvd/server_loop.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 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 <fruit/fruit.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/commands/run_cvd/process_monitor.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+namespace cuttlefish {
+
+class ServerLoop {
+ public:
+ virtual ~ServerLoop();
+ virtual void Run(ProcessMonitor& process_monitor) = 0;
+};
+
+fruit::Component<fruit::Required<const CuttlefishConfig,
+ const CuttlefishConfig::InstanceSpecific>,
+ ServerLoop>
+serverLoopComponent();
+}
diff --git a/host/commands/run_cvd/validate.cpp b/host/commands/run_cvd/validate.cpp
new file mode 100644
index 0000000..567c4e4
--- /dev/null
+++ b/host/commands/run_cvd/validate.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <fruit/fruit.h>
+#include <iostream>
+
+#include "common/libs/utils/network.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/feature.h"
+#include "host/libs/vm_manager/host_configuration.h"
+
+namespace cuttlefish {
+namespace {
+
+using vm_manager::ValidateHostConfiguration;
+
+class ValidateTapDevices : public Feature {
+ public:
+ INJECT(ValidateTapDevices(const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+
+ bool Enabled() const override { return true; }
+ std::string Name() const override { return "ValidateTapDevices"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ auto used_tap_devices = TapInterfacesInUse();
+ if (used_tap_devices.count(instance_.wifi_tap_name())) {
+ LOG(ERROR) << "Wifi TAP device already in use";
+ return false;
+ } else if (used_tap_devices.count(instance_.mobile_tap_name())) {
+ LOG(ERROR) << "Mobile TAP device already in use";
+ return false;
+ } else if (config_.ethernet() &&
+ used_tap_devices.count(instance_.ethernet_tap_name())) {
+ LOG(ERROR) << "Ethernet TAP device already in use";
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+};
+
+class ValidateHostConfigurationFeature : public Feature {
+ public:
+ INJECT(ValidateHostConfigurationFeature()) {}
+
+ bool Enabled() const override {
+#ifndef __ANDROID__
+ return true;
+#else
+ return false;
+#endif
+ }
+ std::string Name() const override { return "ValidateHostConfiguration"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+
+ protected:
+ bool Setup() override {
+ // Check host configuration
+ std::vector<std::string> config_commands;
+ if (!ValidateHostConfiguration(&config_commands)) {
+ LOG(ERROR) << "Validation of user configuration failed";
+ std::cout << "Execute the following to correctly configure:" << std::endl;
+ for (auto& command : config_commands) {
+ std::cout << " " << command << std::endl;
+ }
+ std::cout << "You may need to logout for the changes to take effect"
+ << std::endl;
+ return false;
+ }
+ return true;
+ }
+};
+
+} // namespace
+
+fruit::Component<fruit::Required<const CuttlefishConfig,
+ const CuttlefishConfig::InstanceSpecific>>
+validationComponent() {
+ return fruit::createComponent()
+ .addMultibinding<Feature, ValidateHostConfigurationFeature>()
+ .addMultibinding<Feature, ValidateTapDevices>();
+}
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/size_utils.cpp b/host/commands/run_cvd/validate.h
similarity index 63%
copy from common/libs/utils/size_utils.cpp
copy to host/commands/run_cvd/validate.h
index 9f25445..99c5c07 100644
--- a/common/libs/utils/size_utils.cpp
+++ b/host/commands/run_cvd/validate.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 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,15 +14,17 @@
* limitations under the License.
*/
-#include "common/libs/utils/size_utils.h"
+#pragma once
-#include <unistd.h>
+#include <fruit/fruit.h>
+
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/feature.h"
namespace cuttlefish {
-uint64_t AlignToPowerOf2(uint64_t val, uint8_t align_log) {
- uint64_t align = 1ULL << align_log;
- return ((val + (align - 1)) / align) * align;
-}
+fruit::Component<fruit::Required<const CuttlefishConfig,
+ const CuttlefishConfig::InstanceSpecific>>
+validationComponent();
-} // namespace cuttlefish
+}
diff --git a/host/commands/secure_env/Android.bp b/host/commands/secure_env/Android.bp
index 3ceeda5..bcc10ec 100644
--- a/host/commands/secure_env/Android.bp
+++ b/host/commands/secure_env/Android.bp
@@ -42,12 +42,16 @@
"tpm_keymaster_context.cpp",
"tpm_keymaster_enforcement.cpp",
"tpm_random_source.cpp",
+ "tpm_remote_provisioning_context.cpp",
"tpm_resource_manager.cpp",
"tpm_serialize.cpp",
],
shared_libs: [
"libbase",
+ "libcppbor_external",
+ "libcppcose_rkp",
"libcuttlefish_fs",
+ "libcuttlefish_kernel_log_monitor_utils",
"libcuttlefish_security",
"libcuttlefish_utils",
"libgatekeeper",
diff --git a/host/commands/secure_env/hmac_serializable.cpp b/host/commands/secure_env/hmac_serializable.cpp
index c40b736..db055b0 100644
--- a/host/commands/secure_env/hmac_serializable.cpp
+++ b/host/commands/secure_env/hmac_serializable.cpp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2020 The Android Open Source Project
+// cOpyright (C) 2020 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.
@@ -16,6 +16,8 @@
#include "hmac_serializable.h"
#include <android-base/logging.h>
+#include <optional>
+#include <vector>
#include "host/commands/secure_env/tpm_auth.h"
#include "host/commands/secure_env/tpm_hmac.h"
@@ -23,13 +25,12 @@
HmacSerializable::HmacSerializable(
TpmResourceManager& resource_manager,
std::function<TpmObjectSlot(TpmResourceManager&)> signing_key_fn,
- uint32_t digest_size,
- Serializable* wrapped) :
- resource_manager_(resource_manager),
- signing_key_fn_(signing_key_fn),
- digest_size_(digest_size),
- wrapped_(wrapped) {
-}
+ uint32_t digest_size, Serializable* wrapped, const Serializable* aad)
+ : resource_manager_(resource_manager),
+ signing_key_fn_(signing_key_fn),
+ digest_size_(digest_size),
+ wrapped_(wrapped),
+ aad_(aad) {}
size_t HmacSerializable::SerializedSize() const {
auto digest_size = sizeof(uint32_t) + digest_size_;
@@ -51,13 +52,13 @@
LOG(ERROR) << "Could not retrieve key";
return buf;
}
+ auto maced_data = AppendAad(signed_data, wrapped_size);
+ if (!maced_data) {
+ return buf;
+ }
auto hmac_data =
- TpmHmac(
- resource_manager_,
- key->get(),
- TpmAuth(ESYS_TR_PASSWORD),
- signed_data,
- wrapped_size);
+ TpmHmac(resource_manager_, key->get(), TpmAuth(ESYS_TR_PASSWORD),
+ maced_data->data(), maced_data->size());
if (!hmac_data) {
LOG(ERROR) << "Failed to produce hmac";
return buf;
@@ -99,13 +100,13 @@
LOG(ERROR) << "Could not retrieve key";
return false;
}
+ auto maced_data = AppendAad(signed_data.get(), signed_data_size);
+ if (!maced_data) {
+ return false;
+ }
auto hmac_check =
- TpmHmac(
- resource_manager_,
- key->get(),
- TpmAuth(ESYS_TR_PASSWORD),
- signed_data.get(),
- signed_data_size);
+ TpmHmac(resource_manager_, key->get(), TpmAuth(ESYS_TR_PASSWORD),
+ maced_data->data(), maced_data->size());
if (!hmac_check) {
LOG(ERROR) << "Unable to calculate signature check";
return false;
@@ -125,3 +126,22 @@
return wrapped_->Deserialize(
const_cast<const uint8_t**>(&inner_buf), inner_buf_end);
}
+
+std::optional<std::vector<uint8_t>> HmacSerializable::AppendAad(
+ const uint8_t* sensitive, size_t sensitive_size) const {
+ if (!aad_) {
+ return std::vector<uint8_t>(sensitive, sensitive + sensitive_size);
+ }
+ std::vector<uint8_t> output(sensitive_size + aad_->SerializedSize());
+ std::copy(sensitive, sensitive + sensitive_size, output.begin());
+
+ const uint8_t* actual_output_end =
+ aad_->Serialize(&output[sensitive_size], output.data() + output.size());
+ const ptrdiff_t actual_aad_size = actual_output_end - output.data();
+ if (actual_aad_size != output.size()) {
+ LOG(ERROR) << "Serialized aad did not match expected size. Expected: "
+ << output.size() << ", actual: " << actual_aad_size;
+ return std::nullopt;
+ }
+ return output;
+}
diff --git a/host/commands/secure_env/hmac_serializable.h b/host/commands/secure_env/hmac_serializable.h
index 4b90124..8de17df 100644
--- a/host/commands/secure_env/hmac_serializable.h
+++ b/host/commands/secure_env/hmac_serializable.h
@@ -15,6 +15,9 @@
#pragma once
+#include <optional>
+#include <vector>
+
#include <keymaster/serializable.h>
#include "host/commands/secure_env/tpm_resource_manager.h"
@@ -38,17 +41,21 @@
*/
class HmacSerializable : public keymaster::Serializable {
public:
- HmacSerializable(TpmResourceManager&,
- std::function<TpmObjectSlot(TpmResourceManager&)>,
- uint32_t digest_size,
- Serializable*);
+ HmacSerializable(TpmResourceManager&,
+ std::function<TpmObjectSlot(TpmResourceManager&)>,
+ uint32_t digest_size, Serializable*, const Serializable* aad);
- size_t SerializedSize() const override;
- uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override;
- bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override;
+ size_t SerializedSize() const override;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override;
+
private:
TpmResourceManager& resource_manager_;
std::function<TpmObjectSlot(TpmResourceManager&)> signing_key_fn_;
uint32_t digest_size_;
- keymaster::Serializable* wrapped_;
+ Serializable* wrapped_;
+ const Serializable* aad_;
+
+ std::optional<std::vector<uint8_t>> AppendAad(const uint8_t* sensitive,
+ size_t sensitive_size) const;
};
diff --git a/host/commands/secure_env/json_serializable.cpp b/host/commands/secure_env/json_serializable.cpp
index d4f2fd8..bcca9f0 100644
--- a/host/commands/secure_env/json_serializable.cpp
+++ b/host/commands/secure_env/json_serializable.cpp
@@ -89,8 +89,9 @@
EncryptedSerializable encryption(
resource_manager, parent_key_fn, sensitive_material);
auto signing_key_fn = SigningKeyCreator(kUniqueKey);
- HmacSerializable sign_check(
- resource_manager, signing_key_fn, TPM2_SHA256_DIGEST_SIZE, &encryption);
+ HmacSerializable sign_check(resource_manager, signing_key_fn,
+ TPM2_SHA256_DIGEST_SIZE, &encryption,
+ /*aad=*/nullptr);
auto size = sign_check.SerializedSize();
LOG(INFO) << "size : " << size;
@@ -139,8 +140,9 @@
EncryptedSerializable encryption(
resource_manager, parent_key_fn, sensitive_material);
auto signing_key_fn = SigningKeyCreator(kUniqueKey);
- HmacSerializable sign_check(
- resource_manager, signing_key_fn, TPM2_SHA256_DIGEST_SIZE, &encryption);
+ HmacSerializable sign_check(resource_manager, signing_key_fn,
+ TPM2_SHA256_DIGEST_SIZE, &encryption,
+ /*aad=*/nullptr);
auto buf = reinterpret_cast<const uint8_t*>(buffer.data());
auto buf_end = buf + buffer.size();
diff --git a/host/commands/secure_env/keymaster_responder.cpp b/host/commands/secure_env/keymaster_responder.cpp
index 013d5e7..4936f7c 100644
--- a/host/commands/secure_env/keymaster_responder.cpp
+++ b/host/commands/secure_env/keymaster_responder.cpp
@@ -65,6 +65,9 @@
HANDLE_MESSAGE(DELETE_KEY, DeleteKey)
HANDLE_MESSAGE(DELETE_ALL_KEYS, DeleteAllKeys)
HANDLE_MESSAGE(IMPORT_WRAPPED_KEY, ImportWrappedKey)
+ HANDLE_MESSAGE(GENERATE_RKP_KEY, GenerateRkpKey)
+ HANDLE_MESSAGE(GENERATE_CSR, GenerateCsr)
+ HANDLE_MESSAGE(GENERATE_TIMESTAMP_TOKEN, GenerateTimestampToken)
#undef HANDLE_MESSAGE
#define HANDLE_MESSAGE_W_RETURN(ENUM_NAME, METHOD_NAME) \
case ENUM_NAME: {\
@@ -80,7 +83,10 @@
HANDLE_MESSAGE_W_RETURN(VERIFY_AUTHORIZATION, VerifyAuthorization)
HANDLE_MESSAGE_W_RETURN(DEVICE_LOCKED, DeviceLocked)
HANDLE_MESSAGE_W_RETURN(GET_VERSION_2, GetVersion2)
-#undef HANDLE_MESSAGE
+ HANDLE_MESSAGE_W_RETURN(CONFIGURE_VENDOR_PATCHLEVEL,
+ ConfigureVendorPatchlevel)
+ HANDLE_MESSAGE_W_RETURN(CONFIGURE_BOOT_PATCHLEVEL, ConfigureBootPatchlevel)
+#undef HANDLE_MESSAGE_W_RETURN
#define HANDLE_MESSAGE_W_RETURN_NO_ARG(ENUM_NAME, METHOD_NAME) \
case ENUM_NAME: {\
auto response = keymaster_.METHOD_NAME(); \
@@ -88,7 +94,7 @@
}
HANDLE_MESSAGE_W_RETURN_NO_ARG(GET_HMAC_SHARING_PARAMETERS, GetHmacSharingParameters)
HANDLE_MESSAGE_W_RETURN_NO_ARG(EARLY_BOOT_ENDED, EarlyBootEnded)
-#undef HANDLE_MESSAGE
+#undef HANDLE_MESSAGE_W_RETURN_NO_ARG
case ADD_RNG_ENTROPY: {
AddEntropyRequest request(keymaster_.message_version());
if (!request.Deserialize(&buffer, end)) {
diff --git a/host/commands/secure_env/secure_env.cpp b/host/commands/secure_env/secure_env.cpp
index 7263404..9baaf15 100644
--- a/host/commands/secure_env/secure_env.cpp
+++ b/host/commands/secure_env/secure_env.cpp
@@ -26,11 +26,13 @@
#include "common/libs/fs/shared_fd.h"
#include "common/libs/security/gatekeeper_channel.h"
#include "common/libs/security/keymaster_channel.h"
+#include "host/commands/kernel_log_monitor/kernel_log_server.h"
+#include "host/commands/kernel_log_monitor/utils.h"
#include "host/commands/secure_env/device_tpm.h"
#include "host/commands/secure_env/fragile_tpm_storage.h"
#include "host/commands/secure_env/gatekeeper_responder.h"
-#include "host/commands/secure_env/insecure_fallback_storage.h"
#include "host/commands/secure_env/in_process_tpm.h"
+#include "host/commands/secure_env/insecure_fallback_storage.h"
#include "host/commands/secure_env/keymaster_responder.h"
#include "host/commands/secure_env/soft_gatekeeper.h"
#include "host/commands/secure_env/tpm_gatekeeper.h"
@@ -46,6 +48,11 @@
DEFINE_int32(keymaster_fd_out, -1, "A pipe for keymaster communication");
DEFINE_int32(gatekeeper_fd_in, -1, "A pipe for gatekeeper communication");
DEFINE_int32(gatekeeper_fd_out, -1, "A pipe for gatekeeper communication");
+DEFINE_int32(kernel_events_fd, -1,
+ "A pipe for monitoring events based on "
+ "messages written to the kernel log. This "
+ "is used by secure_env to monitor for "
+ "device reboots.");
DEFINE_string(tpm_impl,
"in_memory",
@@ -57,6 +64,51 @@
DEFINE_string(gatekeeper_impl, "tpm",
"The gatekeeper implementation. \"tpm\" or \"software\"");
+namespace {
+
+// Dup a command line file descriptor into a SharedFD.
+cuttlefish::SharedFD DupFdFlag(gflags::int32 fd) {
+ CHECK(fd != -1);
+ cuttlefish::SharedFD duped = cuttlefish::SharedFD::Dup(fd);
+ CHECK(duped->IsOpen()) << "Could not dup output fd: " << duped->StrError();
+ // The original FD is intentionally kept open so that we can re-exec this
+ // process without having to do a bunch of argv book-keeping.
+ return duped;
+}
+
+// Re-launch this process with all the same flags it was originallys started
+// with.
+[[noreturn]] void ReExecSelf() {
+ // Allocate +1 entry for terminating nullptr.
+ std::vector<char*> argv(gflags::GetArgvs().size() + 1, nullptr);
+ for (size_t i = 0; i < gflags::GetArgvs().size(); ++i) {
+ argv[i] = strdup(gflags::GetArgvs()[i].c_str());
+ CHECK(argv[i] != nullptr) << "OOM";
+ }
+ execv("/proc/self/exe", argv.data());
+ char buf[128];
+ LOG(FATAL) << "Exec failed, secure_env is out of sync with the guest: "
+ << errno << "(" << strerror_r(errno, buf, sizeof(buf)) << ")";
+ abort(); // LOG(FATAL) isn't marked as noreturn
+}
+
+// Spin up a thread that monitors for a kernel loaded event, then re-execs
+// this process. This way, secure_env's boot tracking matches up with the guest.
+std::thread StartKernelEventMonitor(cuttlefish::SharedFD kernel_events_fd) {
+ return std::thread([kernel_events_fd]() {
+ while (kernel_events_fd->IsOpen()) {
+ auto read_result = monitor::ReadEvent(kernel_events_fd);
+ CHECK(read_result.has_value()) << kernel_events_fd->StrError();
+ if (read_result->event == monitor::Event::KernelLoaded) {
+ LOG(DEBUG) << "secure_env detected guest reboot, restarting.";
+ ReExecSelf();
+ }
+ }
+ });
+}
+
+} // namespace
+
int main(int argc, char** argv) {
cuttlefish::DefaultSubprocessLogging(argv);
gflags::ParseCommandLineFlags(&argc, &argv, true);
@@ -114,9 +166,8 @@
keymaster::KeymasterContext* keymaster_context;
if (FLAGS_keymint_impl == "software") {
// TODO: See if this is the right KM version.
- keymaster_context =
- new keymaster::PureSoftKeymasterContext(keymaster::KmVersion::KEYMASTER_4,
- KM_SECURITY_LEVEL_SOFTWARE);
+ keymaster_context = new keymaster::PureSoftKeymasterContext(
+ keymaster::KmVersion::KEYMINT_1, KM_SECURITY_LEVEL_SOFTWARE);
} else if (FLAGS_keymint_impl == "tpm") {
keymaster_context =
new TpmKeymasterContext(*resource_manager, *keymaster_enforcement);
@@ -125,33 +176,19 @@
return -1;
}
keymaster::AndroidKeymaster keymaster{
- keymaster_context, kOperationTableSize};
+ keymaster_context, kOperationTableSize,
+ keymaster::MessageVersion(keymaster::KmVersion::KEYMINT_1,
+ 0 /* km_date */)};
- CHECK(FLAGS_keymaster_fd_in != -1);
- auto keymaster_in = cuttlefish::SharedFD::Dup(FLAGS_keymaster_fd_in);
- CHECK(keymaster_in->IsOpen()) << "Could not dup input fd: "
- << keymaster_in->StrError();
- close(FLAGS_keymaster_fd_in);
+ auto keymaster_in = DupFdFlag(FLAGS_keymaster_fd_in);
+ auto keymaster_out = DupFdFlag(FLAGS_keymaster_fd_out);
+ auto gatekeeper_in = DupFdFlag(FLAGS_gatekeeper_fd_in);
+ auto gatekeeper_out = DupFdFlag(FLAGS_gatekeeper_fd_out);
+ auto kernel_events_fd = DupFdFlag(FLAGS_kernel_events_fd);
- CHECK(FLAGS_keymaster_fd_out != -1);
- auto keymaster_out = cuttlefish::SharedFD::Dup(FLAGS_keymaster_fd_out);
- CHECK(keymaster_out->IsOpen()) << "Could not dup output fd: "
- << keymaster_out->StrError();
- close(FLAGS_keymaster_fd_out);
+ std::vector<std::thread> threads;
- CHECK(FLAGS_gatekeeper_fd_in != -1);
- auto gatekeeper_in = cuttlefish::SharedFD::Dup(FLAGS_gatekeeper_fd_in);
- CHECK(gatekeeper_in->IsOpen()) << "Could not dup input fd: "
- << gatekeeper_in->StrError();
- close(FLAGS_gatekeeper_fd_in);
-
- CHECK(FLAGS_gatekeeper_fd_out != -1);
- auto gatekeeper_out = cuttlefish::SharedFD::Dup(FLAGS_gatekeeper_fd_out);
- CHECK(gatekeeper_out->IsOpen()) << "Could not dup output fd: "
- << keymaster_out->StrError();
- close(FLAGS_gatekeeper_fd_out);
-
- std::thread keymaster_thread([keymaster_in, keymaster_out, &keymaster]() {
+ threads.emplace_back([keymaster_in, keymaster_out, &keymaster]() {
while (true) {
cuttlefish::KeymasterChannel keymaster_channel(
keymaster_in, keymaster_out);
@@ -163,7 +200,7 @@
}
});
- std::thread gatekeeper_thread([gatekeeper_in, gatekeeper_out, &gatekeeper]() {
+ threads.emplace_back([gatekeeper_in, gatekeeper_out, &gatekeeper]() {
while (true) {
cuttlefish::GatekeeperChannel gatekeeper_channel(
gatekeeper_in, gatekeeper_out);
@@ -175,6 +212,9 @@
}
});
- keymaster_thread.join();
- gatekeeper_thread.join();
+ threads.emplace_back(StartKernelEventMonitor(kernel_events_fd));
+
+ for (auto& t : threads) {
+ t.join();
+ }
}
diff --git a/host/commands/secure_env/tpm_attestation_record.cpp b/host/commands/secure_env/tpm_attestation_record.cpp
index f47a0ef..1a26422 100644
--- a/host/commands/secure_env/tpm_attestation_record.cpp
+++ b/host/commands/secure_env/tpm_attestation_record.cpp
@@ -19,22 +19,37 @@
#include <android-base/logging.h>
+namespace {
+using VerifiedBootParams = keymaster::AttestationContext::VerifiedBootParams;
using keymaster::AuthorizationSet;
+VerifiedBootParams MakeVbParams() {
+ // Cuttlefish is hard-coded to verifiedbootstate=orange
+ // See device/google/cuttlefish/host/libs/config/bootconfig_args.cpp
+ VerifiedBootParams vb_params;
+ static uint8_t empty_vb_key[32] = {};
+ vb_params.verified_boot_key = {empty_vb_key, sizeof(empty_vb_key)};
+ vb_params.verified_boot_hash = {empty_vb_key, sizeof(empty_vb_key)};
+ vb_params.verified_boot_state = KM_VERIFIED_BOOT_UNVERIFIED;
+ vb_params.device_locked = false;
+ return vb_params;
+}
+
+} // namespace
+
+TpmAttestationRecordContext::TpmAttestationRecordContext()
+ : keymaster::AttestationContext(::keymaster::KmVersion::KEYMINT_1),
+ vb_params_(MakeVbParams()) {}
+
keymaster_security_level_t TpmAttestationRecordContext::GetSecurityLevel() const {
return KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
}
keymaster_error_t TpmAttestationRecordContext::VerifyAndCopyDeviceIds(
- const AuthorizationSet& attestation_params,
- AuthorizationSet* attestation) const {
+ const AuthorizationSet& /*attestation_params*/,
+ AuthorizationSet* /*attestation*/) const {
LOG(DEBUG) << "TODO(schuffelen): Implement VerifyAndCopyDeviceIds";
- attestation->Difference(attestation_params);
- attestation->Union(attestation_params);
- if (int index = attestation->find(keymaster::TAG_ATTESTATION_APPLICATION_ID)) {
- attestation->erase(index);
- }
- return KM_ERROR_OK;
+ return KM_ERROR_UNIMPLEMENTED;
}
keymaster::Buffer TpmAttestationRecordContext::GenerateUniqueId(
@@ -44,28 +59,11 @@
return {};
}
-const keymaster::AttestationContext::VerifiedBootParams*
-TpmAttestationRecordContext::GetVerifiedBootParams(keymaster_error_t* error) const {
- LOG(DEBUG) << "TODO(schuffelen): Implement GetVerifiedBootParams";
- if (!vb_params_) {
- vb_params_.reset(new VerifiedBootParams{});
-
- // TODO(schuffelen): Get this data out of vbmeta
- static uint8_t fake_vb_key[32];
- static bool fake_vb_key_initialized = false;
- if (!fake_vb_key_initialized) {
- for (int i = 0; i < sizeof(fake_vb_key); i++) {
- fake_vb_key[i] = rand();
- }
- fake_vb_key_initialized = true;
- }
- vb_params_->verified_boot_key = {fake_vb_key, sizeof(fake_vb_key)};
- vb_params_->verified_boot_hash = {fake_vb_key, sizeof(fake_vb_key)};
- vb_params_->verified_boot_state = KM_VERIFIED_BOOT_VERIFIED;
- vb_params_->device_locked = true;
- }
+const VerifiedBootParams* TpmAttestationRecordContext::GetVerifiedBootParams(
+ keymaster_error_t* error) const {
+ static VerifiedBootParams vb_params = MakeVbParams();
*error = KM_ERROR_OK;
- return vb_params_.get();
+ return &vb_params;
}
keymaster::KeymasterKeyBlob
diff --git a/host/commands/secure_env/tpm_attestation_record.h b/host/commands/secure_env/tpm_attestation_record.h
index d5d9b98..c65eef4 100644
--- a/host/commands/secure_env/tpm_attestation_record.h
+++ b/host/commands/secure_env/tpm_attestation_record.h
@@ -21,24 +21,21 @@
class TpmAttestationRecordContext : public keymaster::AttestationContext {
public:
- TpmAttestationRecordContext() : keymaster::AttestationContext(
- ::keymaster::KmVersion::KEYMASTER_4_1) {}
- ~TpmAttestationRecordContext() = default;
+ TpmAttestationRecordContext();
+ ~TpmAttestationRecordContext() = default;
- keymaster_security_level_t GetSecurityLevel() const override;
- keymaster_error_t VerifyAndCopyDeviceIds(
- const keymaster::AuthorizationSet&,
- keymaster::AuthorizationSet*) const override;
- keymaster::Buffer GenerateUniqueId(
- uint64_t, const keymaster_blob_t&, bool, keymaster_error_t*) const override;
- const VerifiedBootParams* GetVerifiedBootParams(
- keymaster_error_t* error) const override;
- keymaster::KeymasterKeyBlob GetAttestationKey(
- keymaster_algorithm_t algorithm, keymaster_error_t* error) const override;
- keymaster::CertificateChain GetAttestationChain(
- keymaster_algorithm_t algorithm,
- keymaster_error_t* error) const override;
-
+ keymaster_security_level_t GetSecurityLevel() const override;
+ keymaster_error_t VerifyAndCopyDeviceIds(
+ const keymaster::AuthorizationSet&,
+ keymaster::AuthorizationSet*) const override;
+ keymaster::Buffer GenerateUniqueId(uint64_t, const keymaster_blob_t&, bool,
+ keymaster_error_t*) const override;
+ const VerifiedBootParams* GetVerifiedBootParams(
+ keymaster_error_t* error) const override;
+ keymaster::KeymasterKeyBlob GetAttestationKey(
+ keymaster_algorithm_t algorithm, keymaster_error_t* error) const override;
+ keymaster::CertificateChain GetAttestationChain(
+ keymaster_algorithm_t algorithm, keymaster_error_t* error) const override;
private:
- mutable std::unique_ptr<VerifiedBootParams> vb_params_;
+ VerifiedBootParams vb_params_;
};
diff --git a/host/commands/secure_env/tpm_key_blob_maker.cpp b/host/commands/secure_env/tpm_key_blob_maker.cpp
index 64f344a..a27ea76 100644
--- a/host/commands/secure_env/tpm_key_blob_maker.cpp
+++ b/host/commands/secure_env/tpm_key_blob_maker.cpp
@@ -41,9 +41,76 @@
static keymaster_error_t SplitEnforcedProperties(
const keymaster::AuthorizationSet& key_description,
keymaster::AuthorizationSet* hw_enforced,
- keymaster::AuthorizationSet* sw_enforced) {
+ keymaster::AuthorizationSet* sw_enforced,
+ keymaster::AuthorizationSet* hidden) {
for (auto& entry : key_description) {
switch (entry.tag) {
+ // These cannot be specified by the client.
+ case KM_TAG_BOOT_PATCHLEVEL:
+ case KM_TAG_ORIGIN:
+ case KM_TAG_OS_PATCHLEVEL:
+ case KM_TAG_OS_VERSION:
+ case KM_TAG_ROOT_OF_TRUST:
+ case KM_TAG_VENDOR_PATCHLEVEL:
+ LOG(ERROR) << "Root of trust and origin tags may not be specified";
+ return KM_ERROR_INVALID_TAG;
+
+ // These are hidden
+ case KM_TAG_APPLICATION_DATA:
+ case KM_TAG_APPLICATION_ID:
+ hidden->push_back(entry);
+ break;
+
+ // These should not be in key descriptions because they're for operation
+ // parameters.
+ case KM_TAG_ASSOCIATED_DATA:
+ case KM_TAG_AUTH_TOKEN:
+ case KM_TAG_CONFIRMATION_TOKEN:
+ case KM_TAG_INVALID:
+ case KM_TAG_MAC_LENGTH:
+ case KM_TAG_NONCE:
+ LOG(ERROR) << "Tag " << entry.tag
+ << " not allowed in key generation/import";
+ break;
+
+ // These are provided to support attestation key generation, but should
+ // not be included in the key characteristics.
+ case KM_TAG_ATTESTATION_APPLICATION_ID:
+ case KM_TAG_ATTESTATION_CHALLENGE:
+ case KM_TAG_ATTESTATION_ID_BRAND:
+ case KM_TAG_ATTESTATION_ID_DEVICE:
+ case KM_TAG_ATTESTATION_ID_IMEI:
+ case KM_TAG_ATTESTATION_ID_MANUFACTURER:
+ case KM_TAG_ATTESTATION_ID_MEID:
+ case KM_TAG_ATTESTATION_ID_MODEL:
+ case KM_TAG_ATTESTATION_ID_PRODUCT:
+ case KM_TAG_ATTESTATION_ID_SERIAL:
+ case KM_TAG_CERTIFICATE_SERIAL:
+ case KM_TAG_CERTIFICATE_SUBJECT:
+ case KM_TAG_CERTIFICATE_NOT_BEFORE:
+ case KM_TAG_CERTIFICATE_NOT_AFTER:
+ case KM_TAG_RESET_SINCE_ID_ROTATION:
+ break;
+
+ // strongbox-only tags
+ case KM_TAG_DEVICE_UNIQUE_ATTESTATION:
+ LOG(ERROR) << "Strongbox-only tag: " << entry.tag;
+ return KM_ERROR_UNSUPPORTED_TAG;
+
+ case KM_TAG_ROLLBACK_RESISTANT:
+ return KM_ERROR_UNSUPPORTED_TAG;
+
+ case KM_TAG_ROLLBACK_RESISTANCE:
+ LOG(ERROR) << "Rollback resistance is not implemented.";
+ return KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE;
+
+ // These are nominally HW tags, but we don't actually support HW key
+ // attestation yet.
+ case KM_TAG_ALLOW_WHILE_ON_BODY:
+ case KM_TAG_EXPORTABLE:
+ case KM_TAG_IDENTITY_CREDENTIAL_KEY:
+ case KM_TAG_STORAGE_KEY:
+
case KM_TAG_PURPOSE:
case KM_TAG_ALGORITHM:
case KM_TAG_KEY_SIZE:
@@ -63,17 +130,32 @@
case KM_TAG_EC_CURVE:
case KM_TAG_ECIES_SINGLE_HASH_MODE:
case KM_TAG_USER_AUTH_TYPE:
- case KM_TAG_ORIGIN:
- case KM_TAG_OS_VERSION:
- case KM_TAG_OS_PATCHLEVEL:
case KM_TAG_EARLY_BOOT_ONLY:
case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
hw_enforced->push_back(entry);
break;
- default:
+
+ // The remaining tags are all software.
+ case KM_TAG_ACTIVE_DATETIME:
+ case KM_TAG_ALL_APPLICATIONS:
+ case KM_TAG_ALL_USERS:
+ case KM_TAG_BOOTLOADER_ONLY:
+ case KM_TAG_CREATION_DATETIME:
+ case KM_TAG_INCLUDE_UNIQUE_ID:
+ case KM_TAG_MAX_BOOT_LEVEL:
+ case KM_TAG_ORIGINATION_EXPIRE_DATETIME:
+ case KM_TAG_RSA_OAEP_MGF_DIGEST:
+ case KM_TAG_TRUSTED_CONFIRMATION_REQUIRED:
+ case KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED:
+ case KM_TAG_UNIQUE_ID:
+ case KM_TAG_USAGE_COUNT_LIMIT:
+ case KM_TAG_USAGE_EXPIRE_DATETIME:
+ case KM_TAG_USER_ID:
sw_enforced->push_back(entry);
+ break;
}
}
+
return KM_ERROR_OK;
}
@@ -102,20 +184,9 @@
KeymasterKeyBlob* blob,
AuthorizationSet* hw_enforced,
AuthorizationSet* sw_enforced) const {
- std::set<keymaster_tag_t> protected_tags = {
- KM_TAG_ROOT_OF_TRUST,
- KM_TAG_ORIGIN,
- KM_TAG_OS_VERSION,
- KM_TAG_OS_PATCHLEVEL,
- };
- for (auto tag : protected_tags) {
- if (key_description.Contains(tag)) {
- LOG(ERROR) << "Invalid tag " << tag;
- return KM_ERROR_INVALID_TAG;
- }
- }
- auto rc =
- SplitEnforcedProperties(key_description, hw_enforced, sw_enforced);
+ AuthorizationSet hidden;
+ auto rc = SplitEnforcedProperties(key_description, hw_enforced, sw_enforced,
+ &hidden);
if (rc != KM_ERROR_OK) {
return rc;
}
@@ -126,12 +197,13 @@
hw_enforced->push_back(keymaster::TAG_OS_PATCHLEVEL, os_patchlevel_);
return UnvalidatedCreateKeyBlob(key_material, *hw_enforced, *sw_enforced,
- blob);
+ hidden, blob);
}
keymaster_error_t TpmKeyBlobMaker::UnvalidatedCreateKeyBlob(
const KeymasterKeyBlob& key_material, const AuthorizationSet& hw_enforced,
- const AuthorizationSet& sw_enforced, KeymasterKeyBlob* blob) const {
+ const AuthorizationSet& sw_enforced, const AuthorizationSet& hidden,
+ KeymasterKeyBlob* blob) const {
keymaster::Buffer key_material_buffer(
key_material.key_material, key_material.key_material_size);
AuthorizationSet hw_enforced_mutable = hw_enforced;
@@ -142,8 +214,12 @@
EncryptedSerializable encryption(
resource_manager_, parent_key_fn, sensitive_material);
auto signing_key_fn = SigningKeyCreator(kUniqueKey);
- HmacSerializable sign_check(
- resource_manager_, signing_key_fn, TPM2_SHA256_DIGEST_SIZE, &encryption);
+ // TODO(b/154956668) The "hidden" tags should also be mixed into the TPM ACL
+ // so that the TPM requires them to be presented to unwrap the key. This is
+ // necessary to meet the requirement that full breach of KeyMint means an
+ // attacker cannot unwrap keys w/o the application id/data.
+ HmacSerializable sign_check(resource_manager_, signing_key_fn,
+ TPM2_SHA256_DIGEST_SIZE, &encryption, &hidden);
auto generated_blob = SerializableToKeyBlob(sign_check);
LOG(VERBOSE) << "Keymaster key size: " << generated_blob.key_material_size;
if (generated_blob.key_material_size != 0) {
@@ -155,9 +231,8 @@
}
keymaster_error_t TpmKeyBlobMaker::UnwrapKeyBlob(
- const keymaster_key_blob_t& blob,
- AuthorizationSet* hw_enforced,
- AuthorizationSet* sw_enforced,
+ const keymaster_key_blob_t& blob, AuthorizationSet* hw_enforced,
+ AuthorizationSet* sw_enforced, const AuthorizationSet& hidden,
KeymasterKeyBlob* key_material) const {
keymaster::Buffer key_material_buffer(blob.key_material_size);
CompositeSerializable sensitive_material(
@@ -166,17 +241,17 @@
EncryptedSerializable encryption(
resource_manager_, parent_key_fn, sensitive_material);
auto signing_key_fn = SigningKeyCreator(kUniqueKey);
- HmacSerializable sign_check(
- resource_manager_, signing_key_fn, TPM2_SHA256_DIGEST_SIZE, &encryption);
+ HmacSerializable sign_check(resource_manager_, signing_key_fn,
+ TPM2_SHA256_DIGEST_SIZE, &encryption, &hidden);
auto buf = blob.key_material;
auto buf_end = buf + blob.key_material_size;
if (!sign_check.Deserialize(&buf, buf_end)) {
LOG(ERROR) << "Failed to deserialize key.";
- return KM_ERROR_UNKNOWN_ERROR;
+ return KM_ERROR_INVALID_KEY_BLOB;
}
if (key_material_buffer.available_read() == 0) {
LOG(ERROR) << "Key material was corrupted and the size was too large";
- return KM_ERROR_UNKNOWN_ERROR;
+ return KM_ERROR_INVALID_KEY_BLOB;
}
*key_material = KeymasterKeyBlob(
key_material_buffer.peek_read(), key_material_buffer.available_read());
diff --git a/host/commands/secure_env/tpm_key_blob_maker.h b/host/commands/secure_env/tpm_key_blob_maker.h
index a0483b4..736e696 100644
--- a/host/commands/secure_env/tpm_key_blob_maker.h
+++ b/host/commands/secure_env/tpm_key_blob_maker.h
@@ -43,6 +43,7 @@
const keymaster::KeymasterKeyBlob& key_material,
const keymaster::AuthorizationSet& hw_enforced,
const keymaster::AuthorizationSet& sw_enforced,
+ const keymaster::AuthorizationSet& hidden,
keymaster::KeymasterKeyBlob* blob) const;
/**
@@ -60,6 +61,7 @@
const keymaster_key_blob_t& blob,
keymaster::AuthorizationSet* hw_enforced,
keymaster::AuthorizationSet* sw_enforced,
+ const keymaster::AuthorizationSet& hidden,
keymaster::KeymasterKeyBlob* key_material) const;
keymaster_error_t SetSystemVersion(uint32_t os_version, uint32_t os_patchlevel);
diff --git a/host/commands/secure_env/tpm_keymaster_context.cpp b/host/commands/secure_env/tpm_keymaster_context.cpp
index 850d48b..1a240d3 100644
--- a/host/commands/secure_env/tpm_keymaster_context.cpp
+++ b/host/commands/secure_env/tpm_keymaster_context.cpp
@@ -28,22 +28,43 @@
#include <keymaster/km_openssl/triple_des_key.h>
#include "host/commands/secure_env/tpm_attestation_record.h"
-#include "host/commands/secure_env/tpm_random_source.h"
#include "host/commands/secure_env/tpm_key_blob_maker.h"
+#include "host/commands/secure_env/tpm_random_source.h"
+#include "host/commands/secure_env/tpm_remote_provisioning_context.h"
+namespace {
using keymaster::AuthorizationSet;
using keymaster::KeymasterKeyBlob;
using keymaster::KeyFactory;
using keymaster::OperationFactory;
+keymaster::AuthorizationSet GetHiddenTags(
+ const AuthorizationSet& authorizations) {
+ keymaster::AuthorizationSet output;
+ keymaster_blob_t entry;
+ if (authorizations.GetTagValue(keymaster::TAG_APPLICATION_ID, &entry)) {
+ output.push_back(keymaster::TAG_APPLICATION_ID, entry.data,
+ entry.data_length);
+ }
+ if (authorizations.GetTagValue(keymaster::TAG_APPLICATION_DATA, &entry)) {
+ output.push_back(keymaster::TAG_APPLICATION_DATA, entry.data,
+ entry.data_length);
+ }
+ return output;
+}
+
+} // namespace
+
TpmKeymasterContext::TpmKeymasterContext(
TpmResourceManager& resource_manager,
keymaster::KeymasterEnforcement& enforcement)
- : resource_manager_(resource_manager)
- , enforcement_(enforcement)
- , key_blob_maker_(new TpmKeyBlobMaker(resource_manager_))
- , random_source_(new TpmRandomSource(resource_manager_.Esys()))
- , attestation_context_(new TpmAttestationRecordContext()) {
+ : resource_manager_(resource_manager),
+ enforcement_(enforcement),
+ key_blob_maker_(new TpmKeyBlobMaker(resource_manager_)),
+ random_source_(new TpmRandomSource(resource_manager_.Esys())),
+ attestation_context_(new TpmAttestationRecordContext()),
+ remote_provisioning_context_(
+ new TpmRemoteProvisioningContext(resource_manager_)) {
key_factories_.emplace(
KM_ALGORITHM_RSA, new keymaster::RsaKeyFactory(*key_blob_maker_, *this));
key_factories_.emplace(
@@ -187,7 +208,7 @@
return key_blob_maker_->UnvalidatedCreateKeyBlob(
key->key_material(), key->hw_enforced(), key->sw_enforced(),
- upgraded_key);
+ GetHiddenTags(upgrade_params), upgraded_key);
}
keymaster_error_t TpmKeymasterContext::ParseKeyBlob(
@@ -198,12 +219,10 @@
keymaster::AuthorizationSet sw_enforced;
keymaster::KeymasterKeyBlob key_material;
- auto rc =
- key_blob_maker_->UnwrapKeyBlob(
- blob,
- &hw_enforced,
- &sw_enforced,
- &key_material);
+ keymaster::AuthorizationSet hidden = GetHiddenTags(additional_params);
+
+ auto rc = key_blob_maker_->UnwrapKeyBlob(blob, &hw_enforced, &sw_enforced,
+ hidden, &key_material);
if (rc != KM_ERROR_OK) {
LOG(ERROR) << "Failed to unwrap key: " << rc;
return rc;
@@ -247,18 +266,20 @@
keymaster::CertificateChain TpmKeymasterContext::GenerateAttestation(
const keymaster::Key& key, const keymaster::AuthorizationSet& attest_params,
- keymaster::UniquePtr<keymaster::Key> /* attest_key */,
- const keymaster::KeymasterBlob& /* issuer_subject */,
+ keymaster::UniquePtr<keymaster::Key> attest_key,
+ const keymaster::KeymasterBlob& issuer_subject,
keymaster_error_t* error) const {
LOG(INFO) << "TODO(b/155697200): Link attestation back to the TPM";
keymaster_algorithm_t key_algorithm;
if (!key.authorizations().GetTagValue(keymaster::TAG_ALGORITHM,
&key_algorithm)) {
+ LOG(ERROR) << "Cannot find key algorithm (TAG_ALGORITHM)";
*error = KM_ERROR_UNKNOWN_ERROR;
return {};
}
if ((key_algorithm != KM_ALGORITHM_RSA && key_algorithm != KM_ALGORITHM_EC)) {
+ LOG(ERROR) << "Invalid algorithm: " << key_algorithm;
*error = KM_ERROR_INCOMPATIBLE_ALGORITHM;
return {};
}
@@ -277,12 +298,20 @@
// hardware/interfaces/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp:203
// at commit 36dcf1a404a9cf07ca5a2a6ad92371507194fe1b .
if (attest_params.find(keymaster::TAG_DEVICE_UNIQUE_ATTESTATION) != -1) {
+ LOG(ERROR) << "TAG_DEVICE_UNIQUE_ATTESTATION not supported";
*error = KM_ERROR_UNIMPLEMENTED;
return {};
}
+ keymaster::AttestKeyInfo attest_key_info(attest_key, &issuer_subject, error);
+ if (*error != KM_ERROR_OK) {
+ LOG(ERROR)
+ << "Error creating attestation key info from given key and subject";
+ return {};
+ }
+
return keymaster::generate_attestation(asymmetric_key, attest_params,
- {} /* attest_key */,
+ std::move(attest_key_info),
*attestation_context_, error);
}
@@ -319,3 +348,8 @@
LOG(ERROR) << "TODO(b/155697375): Implement UnwrapKey";
return KM_ERROR_UNIMPLEMENTED;
}
+
+keymaster::RemoteProvisioningContext*
+TpmKeymasterContext::GetRemoteProvisioningContext() const {
+ return remote_provisioning_context_.get();
+}
diff --git a/host/commands/secure_env/tpm_keymaster_context.h b/host/commands/secure_env/tpm_keymaster_context.h
index 54ba973..30b069d 100644
--- a/host/commands/secure_env/tpm_keymaster_context.h
+++ b/host/commands/secure_env/tpm_keymaster_context.h
@@ -27,6 +27,7 @@
class TpmResourceManager;
class TpmKeyBlobMaker;
class TpmRandomSource;
+class TpmRemoteProvisioningContext;
/**
* Implementation of KeymasterContext that wraps its keys with a TPM.
@@ -41,11 +42,15 @@
std::unique_ptr<TpmKeyBlobMaker> key_blob_maker_;
std::unique_ptr<TpmRandomSource> random_source_;
std::unique_ptr<TpmAttestationRecordContext> attestation_context_;
+ std::unique_ptr<TpmRemoteProvisioningContext> remote_provisioning_context_;
std::map<keymaster_algorithm_t, std::unique_ptr<keymaster::KeyFactory>> key_factories_;
std::vector<keymaster_algorithm_t> supported_algorithms_;
uint32_t os_version_;
uint32_t os_patchlevel_;
-public:
+ std::optional<uint32_t> vendor_patchlevel_;
+ std::optional<uint32_t> boot_patchlevel_;
+
+ public:
TpmKeymasterContext(TpmResourceManager&, keymaster::KeymasterEnforcement&);
~TpmKeymasterContext() = default;
@@ -102,4 +107,35 @@
keymaster::AuthorizationSet* wrapped_key_params,
keymaster_key_format_t* wrapped_key_format,
keymaster::KeymasterKeyBlob* wrapped_key_material) const override;
+
+ keymaster::RemoteProvisioningContext* GetRemoteProvisioningContext()
+ const override;
+
+ keymaster_error_t SetVendorPatchlevel(uint32_t vendor_patchlevel) override {
+ if (vendor_patchlevel_.has_value() &&
+ vendor_patchlevel != vendor_patchlevel_.value()) {
+ // Can't set patchlevel to a different value.
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+ vendor_patchlevel_ = vendor_patchlevel;
+ return KM_ERROR_OK;
+ }
+
+ keymaster_error_t SetBootPatchlevel(uint32_t boot_patchlevel) override {
+ if (boot_patchlevel_.has_value() &&
+ boot_patchlevel != boot_patchlevel_.value()) {
+ // Can't set patchlevel to a different value.
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+ boot_patchlevel_ = boot_patchlevel;
+ return KM_ERROR_OK;
+ }
+
+ std::optional<uint32_t> GetVendorPatchlevel() const override {
+ return vendor_patchlevel_;
+ }
+
+ std::optional<uint32_t> GetBootPatchlevel() const override {
+ return boot_patchlevel_;
+ }
};
diff --git a/host/commands/secure_env/tpm_keymaster_enforcement.cpp b/host/commands/secure_env/tpm_keymaster_enforcement.cpp
index f5b8903..fe47c07 100644
--- a/host/commands/secure_env/tpm_keymaster_enforcement.cpp
+++ b/host/commands/secure_env/tpm_keymaster_enforcement.cpp
@@ -294,18 +294,38 @@
return response;
}
+keymaster_error_t TpmKeymasterEnforcement::GenerateTimestampToken(
+ keymaster::TimestampToken* token) {
+ token->timestamp = get_current_time_ms();
+ token->security_level = SecurityLevel();
+ token->mac = KeymasterBlob();
+
+ auto signing_key_builder = PrimaryKeyBuilder();
+ signing_key_builder.SigningKey();
+ signing_key_builder.UniqueData("timestamp_token");
+ auto signing_key = signing_key_builder.CreateKey(resource_manager_);
+ if (!signing_key) {
+ LOG(ERROR) << "Could not make signing key for verifying authorization";
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ std::vector<uint8_t> token_buf_to_sign(token->SerializedSize(), 0);
+ auto hmac =
+ TpmHmac(resource_manager_, signing_key->get(), TpmAuth(ESYS_TR_PASSWORD),
+ token_buf_to_sign.data(), token_buf_to_sign.size());
+ if (!hmac) {
+ LOG(ERROR) << "Could not calculate timestamp token hmac";
+ return KM_ERROR_UNKNOWN_ERROR;
+ } else if (hmac->size == 0) {
+ LOG(ERROR) << "hmac was too short";
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ token->mac = KeymasterBlob(hmac->buffer, hmac->size);
+
+ return KM_ERROR_OK;
+}
+
bool TpmKeymasterEnforcement::CreateKeyId(
const keymaster_key_blob_t& key_blob, km_id_t* keyid) const {
- keymaster::AuthorizationSet hw_enforced;
- keymaster::AuthorizationSet sw_enforced;
- keymaster::KeymasterKeyBlob key_material;
- auto rc =
- TpmKeyBlobMaker(resource_manager_)
- .UnwrapKeyBlob(key_blob, &hw_enforced, &sw_enforced, &key_material);
- if (rc != KM_ERROR_OK) {
- LOG(ERROR) << "Could not unwrap key: " << rc;
- return false;
- }
auto signing_key_builder = PrimaryKeyBuilder();
signing_key_builder.SigningKey();
signing_key_builder.UniqueData("key_id");
@@ -314,12 +334,9 @@
LOG(ERROR) << "Could not make signing key for key id";
return false;
}
- auto hmac = TpmHmac(
- resource_manager_,
- signing_key->get(),
- TpmAuth(ESYS_TR_PASSWORD),
- key_material.key_material,
- key_material.key_material_size);
+ auto hmac =
+ TpmHmac(resource_manager_, signing_key->get(), TpmAuth(ESYS_TR_PASSWORD),
+ key_blob.key_material, key_blob.key_material_size);
if (!hmac) {
LOG(ERROR) << "Failed to make a signature for a key id";
return false;
diff --git a/host/commands/secure_env/tpm_keymaster_enforcement.h b/host/commands/secure_env/tpm_keymaster_enforcement.h
index 1d6a1e5..3765c06 100644
--- a/host/commands/secure_env/tpm_keymaster_enforcement.h
+++ b/host/commands/secure_env/tpm_keymaster_enforcement.h
@@ -49,6 +49,9 @@
keymaster::VerifyAuthorizationResponse VerifyAuthorization(
const keymaster::VerifyAuthorizationRequest& request) override;
+ keymaster_error_t GenerateTimestampToken(
+ keymaster::TimestampToken* token) override;
+
bool CreateKeyId(
const keymaster_key_blob_t& key_blob,
keymaster::km_id_t* keyid) const override;
diff --git a/host/commands/secure_env/tpm_random_source.cpp b/host/commands/secure_env/tpm_random_source.cpp
index 8695e4a..7849eba 100644
--- a/host/commands/secure_env/tpm_random_source.cpp
+++ b/host/commands/secure_env/tpm_random_source.cpp
@@ -62,6 +62,11 @@
keymaster_error_t TpmRandomSource::AddRngEntropy(
const uint8_t* buffer, size_t size) const {
+ if (size > 2048) {
+ // IKeyMintDevice.aidl specifies that there's an upper limit of 2KiB.
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+
TPM2B_SENSITIVE_DATA in_data;
while (size > MAX_STIR_RANDOM_BUFFER_SIZE) {
memcpy(in_data.buffer, buffer, MAX_STIR_RANDOM_BUFFER_SIZE);
diff --git a/host/commands/secure_env/tpm_remote_provisioning_context.cpp b/host/commands/secure_env/tpm_remote_provisioning_context.cpp
new file mode 100644
index 0000000..38bbdc0
--- /dev/null
+++ b/host/commands/secure_env/tpm_remote_provisioning_context.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2021 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 <algorithm>
+#include <cassert>
+#include <optional>
+
+#include <android-base/logging.h>
+#include <keymaster/cppcose/cppcose.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/hkdf.h>
+#include <openssl/rand.h>
+
+#include "host/commands/secure_env/primary_key_builder.h"
+#include "host/commands/secure_env/tpm_hmac.h"
+#include "tpm_remote_provisioning_context.h"
+
+using namespace cppcose;
+
+TpmRemoteProvisioningContext::TpmRemoteProvisioningContext(
+ TpmResourceManager& resource_manager)
+ : resource_manager_(resource_manager) {
+ std::tie(devicePrivKey_, bcc_) = GenerateBcc(/*testMode=*/false);
+}
+
+std::vector<uint8_t> TpmRemoteProvisioningContext::DeriveBytesFromHbk(
+ const std::string& context, size_t num_bytes) const {
+ // TODO(182928606) Derive using TPM-held secret.
+ std::vector<uint8_t> fakeHbk(32);
+ std::vector<uint8_t> result(num_bytes);
+ if (!HKDF(result.data(), num_bytes, //
+ EVP_sha256(), //
+ fakeHbk.data(), fakeHbk.size(), //
+ nullptr /* salt */, 0 /* salt len */, //
+ reinterpret_cast<const uint8_t*>(context.data()), context.size())) {
+ // Should never fail. Even if it could the API has no way of reporting the
+ // error.
+ LOG(ERROR) << "Error calculating HMAC: " << ERR_peek_last_error();
+ }
+
+ return result;
+}
+
+std::unique_ptr<cppbor::Map> TpmRemoteProvisioningContext::CreateDeviceInfo()
+ const {
+ auto result = std::make_unique<cppbor::Map>();
+ result->add(cppbor::Tstr("brand"), cppbor::Tstr("Google"));
+ result->add(cppbor::Tstr("manufacturer"), cppbor::Tstr("Google"));
+ result->add(cppbor::Tstr("product"),
+ cppbor::Tstr("Cuttlefish Virtual Device"));
+ result->canonicalize();
+ return result;
+}
+
+std::pair<std::vector<uint8_t> /* privKey */, cppbor::Array /* BCC */>
+TpmRemoteProvisioningContext::GenerateBcc(bool testMode) const {
+ std::vector<uint8_t> privKey(ED25519_PRIVATE_KEY_LEN);
+ std::vector<uint8_t> pubKey(ED25519_PUBLIC_KEY_LEN);
+
+ // Length is hard-coded in the BoringCrypto API without a constant
+ std::vector<uint8_t> seed;
+ if (testMode) {
+ seed.resize(32);
+ RAND_bytes(seed.data(), seed.size());
+ } else {
+ seed = DeriveBytesFromHbk("Device Key Seed", 32);
+ }
+ ED25519_keypair_from_seed(pubKey.data(), privKey.data(), seed.data());
+
+ auto coseKey = cppbor::Map()
+ .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+ .add(CoseKey::ALGORITHM, EDDSA)
+ .add(CoseKey::CURVE, ED25519)
+ .add(CoseKey::KEY_OPS, VERIFY)
+ .add(CoseKey::PUBKEY_X, pubKey)
+ .canonicalize();
+ auto sign1Payload =
+ cppbor::Map()
+ .add(1 /* Issuer */, "Issuer")
+ .add(2 /* Subject */, "Subject")
+ .add(-4670552 /* Subject Pub Key */, coseKey.encode())
+ .add(-4670553 /* Key Usage (little-endian order) */,
+ std::vector<uint8_t>{0x20} /* keyCertSign = 1<<5 */)
+ .canonicalize()
+ .encode();
+ auto coseSign1 = constructCoseSign1(privKey, /* signing key */
+ cppbor::Map(), /* extra protected */
+ sign1Payload, {} /* AAD */);
+ assert(coseSign1);
+
+ return {privKey,
+ cppbor::Array().add(std::move(coseKey)).add(coseSign1.moveValue())};
+}
+
+std::optional<cppcose::HmacSha256>
+TpmRemoteProvisioningContext::GenerateHmacSha256(
+ const cppcose::bytevec& input) const {
+ auto signing_key_builder = PrimaryKeyBuilder();
+ signing_key_builder.SigningKey();
+ signing_key_builder.UniqueData("Public Key Authentication Key");
+ auto signing_key = signing_key_builder.CreateKey(resource_manager_);
+ if (!signing_key) {
+ LOG(ERROR) << "Could not make MAC key for authenticating the pubkey";
+ return std::nullopt;
+ }
+
+ auto tpm_digest =
+ TpmHmac(resource_manager_, signing_key->get(), TpmAuth(ESYS_TR_PASSWORD),
+ input.data(), input.size());
+
+ if (!tpm_digest) {
+ LOG(ERROR) << "Could not calculate hmac";
+ return std::nullopt;
+ }
+
+ cppcose::HmacSha256 hmac;
+ if (tpm_digest->size != hmac.size()) {
+ LOG(ERROR) << "TPM-generated digest was too short. Actual size: "
+ << tpm_digest->size << " expected " << hmac.size() << " bytes";
+ return std::nullopt;
+ }
+
+ std::copy(tpm_digest->buffer, tpm_digest->buffer + tpm_digest->size,
+ hmac.begin());
+ return hmac;
+}
diff --git a/host/commands/secure_env/tpm_remote_provisioning_context.h b/host/commands/secure_env/tpm_remote_provisioning_context.h
new file mode 100644
index 0000000..4f285ab
--- /dev/null
+++ b/host/commands/secure_env/tpm_remote_provisioning_context.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 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 <keymaster/remote_provisioning_context.h>
+
+#include "host/commands/secure_env/tpm_resource_manager.h"
+#include "keymaster/cppcose/cppcose.h"
+
+/**
+ * TPM-backed implementation of the provisioning context.
+ */
+class TpmRemoteProvisioningContext
+ : public keymaster::RemoteProvisioningContext {
+ public:
+ TpmRemoteProvisioningContext(TpmResourceManager& resource_manager);
+ ~TpmRemoteProvisioningContext() override = default;
+ std::vector<uint8_t> DeriveBytesFromHbk(const std::string& context,
+ size_t numBytes) const override;
+ std::unique_ptr<cppbor::Map> CreateDeviceInfo() const override;
+ std::pair<std::vector<uint8_t>, cppbor::Array> GenerateBcc(
+ bool testMode) const override;
+ std::optional<cppcose::HmacSha256> GenerateHmacSha256(
+ const cppcose::bytevec& input) const override;
+
+ private:
+ TpmResourceManager& resource_manager_;
+};
diff --git a/host/commands/launch/Android.bp b/host/commands/start/Android.bp
similarity index 95%
rename from host/commands/launch/Android.bp
rename to host/commands/start/Android.bp
index 6adf7a2..6343f14 100644
--- a/host/commands/launch/Android.bp
+++ b/host/commands/start/Android.bp
@@ -18,11 +18,11 @@
}
cc_binary {
- name: "launch_cvd",
+ name: "cvd_internal_start",
srcs: [
"filesystem_explorer.cc",
"flag_forwarder.cc",
- "launch_cvd.cc",
+ "main.cc",
],
shared_libs: [
"libcuttlefish_fs",
diff --git a/host/commands/launch/filesystem_explorer.cc b/host/commands/start/filesystem_explorer.cc
similarity index 100%
rename from host/commands/launch/filesystem_explorer.cc
rename to host/commands/start/filesystem_explorer.cc
diff --git a/host/commands/launch/filesystem_explorer.h b/host/commands/start/filesystem_explorer.h
similarity index 100%
rename from host/commands/launch/filesystem_explorer.h
rename to host/commands/start/filesystem_explorer.h
diff --git a/host/commands/launch/flag_forwarder.cc b/host/commands/start/flag_forwarder.cc
similarity index 100%
rename from host/commands/launch/flag_forwarder.cc
rename to host/commands/start/flag_forwarder.cc
diff --git a/host/commands/launch/flag_forwarder.h b/host/commands/start/flag_forwarder.h
similarity index 100%
rename from host/commands/launch/flag_forwarder.h
rename to host/commands/start/flag_forwarder.h
diff --git a/host/commands/launch/launch_cvd.cc b/host/commands/start/main.cc
similarity index 98%
rename from host/commands/launch/launch_cvd.cc
rename to host/commands/start/main.cc
index f67ce48..c11ac67 100644
--- a/host/commands/launch/launch_cvd.cc
+++ b/host/commands/start/main.cc
@@ -23,13 +23,12 @@
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/subprocess.h"
-#include "host/commands/launch/filesystem_explorer.h"
+#include "host/commands/start/filesystem_explorer.h"
+#include "host/commands/start/flag_forwarder.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/config/host_tools_version.h"
#include "host/libs/config/fetcher_config.h"
-#include "flag_forwarder.h"
-
/**
* If stdin is a tty, that means a user is invoking launch_cvd on the command
* line and wants automatic file detection for assemble_cvd.
diff --git a/host/commands/cvd_host_bugreport/Android.bp b/host/commands/status/Android.bp
similarity index 94%
copy from host/commands/cvd_host_bugreport/Android.bp
copy to host/commands/status/Android.bp
index 4f6aeda..548c8cf 100644
--- a/host/commands/cvd_host_bugreport/Android.bp
+++ b/host/commands/status/Android.bp
@@ -18,7 +18,7 @@
}
cc_binary {
- name: "cvd_host_bugreport",
+ name: "cvd_internal_status",
srcs: [
"main.cc",
],
@@ -27,7 +27,6 @@
"libcuttlefish_fs",
"libcuttlefish_utils",
"libjsoncpp",
- "libziparchive",
],
static_libs: [
"libcuttlefish_host_config",
diff --git a/host/commands/status/main.cc b/host/commands/status/main.cc
new file mode 100644
index 0000000..1ae5949
--- /dev/null
+++ b/host/commands/status/main.cc
@@ -0,0 +1,115 @@
+/*
+ * 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 <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <fstream>
+#include <iomanip>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+#include "common/libs/utils/environment.h"
+#include "common/libs/utils/flag_parser.h"
+#include "common/libs/utils/tee_logging.h"
+#include "host/commands/run_cvd/runner_defs.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/vm_manager/vm_manager.h"
+
+namespace cuttlefish {
+
+int CvdStatusMain(int argc, char** argv) {
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ ::android::base::SetLogger(LogToStderrAndFiles({}));
+
+ std::vector<Flag> flags;
+
+ std::int32_t wait_for_launcher;
+ flags.emplace_back(
+ GflagsCompatFlag("wait_for_launcher", wait_for_launcher)
+ .Help("How many seconds to wait for the launcher to respond to the "
+ "status command. A value of zero means wait indefinitely"));
+
+ flags.emplace_back(HelpFlag(flags));
+ flags.emplace_back(UnexpectedArgumentGuard());
+
+ std::vector<std::string> args =
+ ArgsToVec(argc - 1, argv + 1); // Skip argv[0]
+ CHECK(ParseFlags(flags, args)) << "Could not process command line flags.";
+
+ auto config = CuttlefishConfig::Get();
+ CHECK(config) << "Failed to obtain config object";
+
+ auto instance = config->ForDefaultInstance();
+ auto monitor_path = instance.launcher_monitor_socket_path();
+ CHECK(!monitor_path.empty()) << "No path to launcher monitor found";
+
+ auto monitor_socket = SharedFD::SocketLocalClient(
+ monitor_path.c_str(), false, SOCK_STREAM, wait_for_launcher);
+ CHECK(monitor_socket->IsOpen())
+ << "Unable to connect to launcher monitor at " << monitor_path << ": "
+ << monitor_socket->StrError();
+
+ auto request = LauncherAction::kStatus;
+ auto bytes_sent = monitor_socket->Send(&request, sizeof(request), 0);
+ CHECK(bytes_sent > 0) << "Error sending launcher monitor the status command: "
+ << monitor_socket->StrError();
+
+ // Perform a select with a timeout to guard against launcher hanging
+ SharedFDSet read_set;
+ read_set.Set(monitor_socket);
+ struct timeval timeout = {wait_for_launcher, 0};
+ int selected = Select(&read_set, nullptr, nullptr,
+ wait_for_launcher <= 0 ? nullptr : &timeout);
+ CHECK(selected >= 0) << "Failed communication with the launcher monitor: "
+ << strerror(errno);
+ CHECK(selected > 0)
+ << "Timeout expired waiting for launcher monitor to respond";
+
+ LauncherResponse response;
+ auto bytes_recv = monitor_socket->Recv(&response, sizeof(response), 0);
+ CHECK(bytes_recv > 0) << "Error receiving response from launcher monitor: "
+ << monitor_socket->StrError();
+ CHECK(response == LauncherResponse::kSuccess)
+ << "Received '" << static_cast<char>(response)
+ << "' response from launcher monitor";
+
+ LOG(INFO) << "run_cvd is active.";
+ return 0;
+}
+
+} // namespace cuttlefish
+
+int main(int argc, char** argv) {
+ return cuttlefish::CvdStatusMain(argc, argv);
+}
diff --git a/host/commands/stop_cvd/Android.bp b/host/commands/stop/Android.bp
similarity index 96%
rename from host/commands/stop_cvd/Android.bp
rename to host/commands/stop/Android.bp
index a670a25..306a711 100644
--- a/host/commands/stop_cvd/Android.bp
+++ b/host/commands/stop/Android.bp
@@ -18,7 +18,7 @@
}
cc_binary {
- name: "stop_cvd",
+ name: "cvd_internal_stop",
srcs: [
"main.cc",
],
diff --git a/host/commands/stop_cvd/main.cc b/host/commands/stop/main.cc
similarity index 100%
rename from host/commands/stop_cvd/main.cc
rename to host/commands/stop/main.cc
diff --git a/host/commands/tapsetiff/tapsetiff.py b/host/commands/tapsetiff/tapsetiff.py
deleted file mode 100755
index 3e7c9e4..0000000
--- a/host/commands/tapsetiff/tapsetiff.py
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/python
-
-# Copyright (C) 2020 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.
-
-import fcntl
-import struct
-import sys
-
-TUNSETIFF = 0x400454ca
-IFF_TAP = 0x0002
-IFF_NO_PI = 0x1000
-IFF_VNET_HDR = 0x4000
-
-tun_fd = int(sys.argv[1])
-tap_name = sys.argv[2]
-
-ifr = struct.pack('16sH', tap_name, IFF_TAP | IFF_NO_PI | IFF_VNET_HDR)
-fcntl.ioctl(tun_fd, TUNSETIFF, ifr)
diff --git a/host/commands/tombstone_receiver/main.cpp b/host/commands/tombstone_receiver/main.cpp
index 2e0ea69..5fa495d 100644
--- a/host/commands/tombstone_receiver/main.cpp
+++ b/host/commands/tombstone_receiver/main.cpp
@@ -22,23 +22,21 @@
#include <iomanip>
#include <sstream>
-#include "host/libs/config/logging.h"
#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/flag_parser.h"
+#include "common/libs/utils/shared_fd_flag.h"
+#include "host/libs/config/logging.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");
-DEFINE_string(tombstone_dir, "", "directory to write out tombstones in");
+namespace cuttlefish {
static uint num_tombstones_in_last_second = 0;
static std::string last_tombstone_name = "";
-static std::string next_tombstone_path() {
+static std::string next_tombstone_path(const std::string& tombstone_dir) {
auto in_time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::stringstream ss;
- ss << FLAGS_tombstone_dir << "/tombstone_" <<
- std::put_time(std::gmtime(&in_time_t), "%Y-%m-%d-%H%M%S");
+ ss << tombstone_dir << "/tombstone_"
+ << std::put_time(std::gmtime(&in_time_t), "%Y-%m-%d-%H%M%S");
auto retval = ss.str();
// Gives tombstones unique names
@@ -54,23 +52,38 @@
return retval;
}
-#define CHUNK_RECV_MAX_LEN (1024)
-int main(int argc, char** argv) {
- cuttlefish::DefaultSubprocessLogging(argv);
- google::ParseCommandLineFlags(&argc, &argv, true);
+static constexpr size_t CHUNK_RECV_MAX_LEN = 1024;
- cuttlefish::SharedFD server_fd = cuttlefish::SharedFD::Dup(FLAGS_server_fd);
- close(FLAGS_server_fd);
+int TombstoneReceiverMain(int argc, char** argv) {
+ DefaultSubprocessLogging(argv);
- CHECK(server_fd->IsOpen()) << "Error inheriting tombstone server: "
- << server_fd->StrError();
+ std::vector<Flag> flags;
+
+ std::string tombstone_dir;
+ flags.emplace_back(GflagsCompatFlag("tombstone_dir", tombstone_dir)
+ .Help("directory to write out tombstones in"));
+
+ SharedFD server_fd;
+ flags.emplace_back(
+ SharedFDFlag("server_fd", server_fd)
+ .Help("File descriptor to an already created vsock server"));
+
+ flags.emplace_back(HelpFlag(flags));
+ flags.emplace_back(UnexpectedArgumentGuard());
+
+ std::vector<std::string> args =
+ ArgsToVec(argc - 1, argv + 1); // Skip argv[0]
+ CHECK(ParseFlags(flags, args)) << "Could not process command line flags.";
+
+ CHECK(server_fd->IsOpen()) << "Did not receive a server fd";
+
LOG(DEBUG) << "Host is starting server on port "
<< server_fd->VsockServerPort();
// Server loop
while (true) {
- auto conn = cuttlefish::SharedFD::Accept(*server_fd);
- std::ofstream file(next_tombstone_path(),
+ auto conn = SharedFD::Accept(*server_fd);
+ std::ofstream file(next_tombstone_path(tombstone_dir),
std::ofstream::out | std::ofstream::binary);
while (file.is_open()) {
@@ -87,3 +100,9 @@
return 0;
}
+
+} // namespace cuttlefish
+
+int main(int argc, char** argv) {
+ return cuttlefish::TombstoneReceiverMain(argc, argv);
+}
diff --git a/host/frontend/vnc_server/Android.bp b/host/frontend/vnc_server/Android.bp
index c098e34..468590b 100644
--- a/host/frontend/vnc_server/Android.bp
+++ b/host/frontend/vnc_server/Android.bp
@@ -36,15 +36,24 @@
"libjsoncpp",
"liblog",
],
+ header_libs: [
+ "libcuttlefish_confui_host_headers",
+ ],
static_libs: [
"libcuttlefish_host_config",
"libcuttlefish_screen_connector",
"libcuttlefish_wayland_server",
+ "libcuttlefish_confui",
+ "libcuttlefish_confui_host",
+ "libft2.nodep",
+ "libteeui",
+ "libteeui_localization",
"libffi",
"libjpeg",
"libgflags",
- "libwayland_server",
+ "libwayland_crosvm_gpu_display_extension_server_protocols",
"libwayland_extension_server_protocols",
+ "libwayland_server",
],
defaults: ["cuttlefish_host"],
}
diff --git a/host/frontend/vnc_server/frame_buffer_watcher.cpp b/host/frontend/vnc_server/frame_buffer_watcher.cpp
index 697a6d9..482f173 100644
--- a/host/frontend/vnc_server/frame_buffer_watcher.cpp
+++ b/host/frontend/vnc_server/frame_buffer_watcher.cpp
@@ -27,12 +27,12 @@
#include <android-base/logging.h>
#include "host/frontend/vnc_server/vnc_utils.h"
-#include "host/libs/screen_connector/screen_connector.h"
using cuttlefish::vnc::FrameBufferWatcher;
-FrameBufferWatcher::FrameBufferWatcher(BlackBoard* bb)
- : bb_{bb}, hwcomposer{bb_} {
+FrameBufferWatcher::FrameBufferWatcher(BlackBoard* bb,
+ ScreenConnector& screen_connector)
+ : bb_{bb}, hwcomposer{screen_connector} {
for (auto& stripes_vec : stripes_) {
std::generate_n(std::back_inserter(stripes_vec),
SimulatedHWComposer::NumberOfStripes(),
diff --git a/host/frontend/vnc_server/frame_buffer_watcher.h b/host/frontend/vnc_server/frame_buffer_watcher.h
index cbace19..9b559b6 100644
--- a/host/frontend/vnc_server/frame_buffer_watcher.h
+++ b/host/frontend/vnc_server/frame_buffer_watcher.h
@@ -26,12 +26,14 @@
#include "host/frontend/vnc_server/blackboard.h"
#include "host/frontend/vnc_server/jpeg_compressor.h"
#include "host/frontend/vnc_server/simulated_hw_composer.h"
+#include "host/libs/screen_connector/screen_connector.h"
namespace cuttlefish {
namespace vnc {
class FrameBufferWatcher {
public:
- explicit FrameBufferWatcher(BlackBoard* bb);
+ explicit FrameBufferWatcher(BlackBoard* bb,
+ ScreenConnector& screen_connector);
FrameBufferWatcher(const FrameBufferWatcher&) = delete;
FrameBufferWatcher& operator=(const FrameBufferWatcher&) = delete;
~FrameBufferWatcher();
@@ -72,7 +74,7 @@
mutable std::mutex m_;
bool closed_ GUARDED_BY(m_){};
BlackBoard* bb_{};
- SimulatedHWComposer hwcomposer{bb_};
+ SimulatedHWComposer hwcomposer;
};
} // namespace vnc
diff --git a/host/frontend/vnc_server/main.cpp b/host/frontend/vnc_server/main.cpp
index 58c1da6..e036b40 100644
--- a/host/frontend/vnc_server/main.cpp
+++ b/host/frontend/vnc_server/main.cpp
@@ -15,21 +15,40 @@
*/
#include <algorithm>
+#include <memory>
#include <string>
#include <gflags/gflags.h>
+#include "host/frontend/vnc_server/simulated_hw_composer.h"
#include "host/frontend/vnc_server/vnc_server.h"
#include "host/frontend/vnc_server/vnc_utils.h"
#include "host/libs/config/logging.h"
+#include "host/libs/confui/host_mode_ctrl.h"
+#include "host/libs/confui/host_server.h"
DEFINE_bool(agressive, false, "Whether to use agressive server");
+DEFINE_int32(frame_server_fd, -1, "");
DEFINE_int32(port, 6444, "Port where to listen for connections");
int main(int argc, char* argv[]) {
cuttlefish::DefaultSubprocessLogging(argv);
google::ParseCommandLineFlags(&argc, &argv, true);
- cuttlefish::vnc::VncServer vnc_server(FLAGS_port, FLAGS_agressive);
+ auto& host_mode_ctrl = cuttlefish::HostModeCtrl::Get();
+ auto screen_connector_ptr = cuttlefish::vnc::ScreenConnector::Get(
+ FLAGS_frame_server_fd, host_mode_ctrl);
+ auto& screen_connector = *(screen_connector_ptr.get());
+
+ // create confirmation UI service, giving host_mode_ctrl and
+ // screen_connector
+ // keep this singleton object alive until the webRTC process ends
+ static auto& host_confui_server =
+ cuttlefish::confui::HostServer::Get(host_mode_ctrl, screen_connector);
+
+ host_confui_server.Start();
+ // lint does not like the spelling of "agressive", so needs NOTYPO
+ cuttlefish::vnc::VncServer vnc_server(FLAGS_port, FLAGS_agressive, // NOTYPO
+ screen_connector, host_confui_server);
vnc_server.MainLoop();
}
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index ec1df09..bd2223e 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -16,25 +16,21 @@
#include "host/frontend/vnc_server/simulated_hw_composer.h"
-#include <gflags/gflags.h>
-
#include "host/frontend/vnc_server/vnc_utils.h"
#include "host/libs/config/cuttlefish_config.h"
-DEFINE_int32(frame_server_fd, -1, "");
-
using cuttlefish::vnc::SimulatedHWComposer;
+using ScreenConnector = cuttlefish::vnc::ScreenConnector;
-SimulatedHWComposer::SimulatedHWComposer(BlackBoard* bb)
+SimulatedHWComposer::SimulatedHWComposer(ScreenConnector& screen_connector)
:
#ifdef FUZZ_TEST_VNC
engine_{std::random_device{}()},
#endif
- bb_{bb},
stripes_(kMaxQueueElements, &SimulatedHWComposer::EraseHalfOfElements),
- screen_connector_(ScreenConnector::Get(FLAGS_frame_server_fd)) {
+ screen_connector_(screen_connector) {
stripe_maker_ = std::thread(&SimulatedHWComposer::MakeStripes, this);
- screen_connector_->SetCallback(std::move(GetScreenConnectorCallback()));
+ screen_connector_.SetCallback(std::move(GetScreenConnectorCallback()));
}
SimulatedHWComposer::~SimulatedHWComposer() {
@@ -75,40 +71,40 @@
SimulatedHWComposer::GenerateProcessedFrameCallback
SimulatedHWComposer::GetScreenConnectorCallback() {
- return [](std::uint32_t display_number, std::uint8_t* frame_pixels,
+ return [](std::uint32_t display_number, std::uint32_t frame_w,
+ std::uint32_t frame_h, std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_bytes,
cuttlefish::vnc::VncScProcessedFrame& processed_frame) {
+ processed_frame.display_number_ = display_number;
// TODO(171305898): handle multiple displays.
if (display_number != 0) {
- processed_frame.is_success_ = false;
- return;
+ // BUG 186580833: display_number comes from surface_id in crosvm
+ // create_surface from virtio_gpu.rs set_scanout. We cannot use it as
+ // the display number. Either crosvm virtio-gpu is incorrectly ignoring
+ // scanout id and instead using a monotonically increasing surface id
+ // number as the scanout resource is replaced over time, or frontend code
+ // here is incorrectly assuming surface id == display id.
+ display_number = 0;
}
- const std::uint32_t display_w =
- SimulatedHWComposer::ScreenConnector::ScreenWidth(display_number);
- const std::uint32_t display_h =
- SimulatedHWComposer::ScreenConnector::ScreenHeight(display_number);
- const std::uint32_t display_stride_bytes =
- SimulatedHWComposer::ScreenConnector::ScreenStrideBytes(display_number);
- const std::uint32_t display_bpp =
- SimulatedHWComposer::ScreenConnector::BytesPerPixel();
- const std::uint32_t display_size_bytes =
- SimulatedHWComposer::ScreenConnector::ScreenSizeInBytes(display_number);
+
+ const std::uint32_t frame_bpp = 4;
+ const std::uint32_t frame_size_bytes = frame_h * frame_stride_bytes;
auto& raw_screen = processed_frame.raw_screen_;
- raw_screen.assign(frame_pixels, frame_pixels + display_size_bytes);
+ raw_screen.assign(frame_bytes, frame_bytes + frame_size_bytes);
static std::uint32_t next_frame_number = 0;
const auto num_stripes = SimulatedHWComposer::kNumStripes;
for (int i = 0; i < num_stripes; ++i) {
- std::uint16_t y = (display_h / num_stripes) * i;
+ std::uint16_t y = (frame_h / num_stripes) * i;
// Last frames on the right and/or bottom handle extra pixels
// when a screen dimension is not evenly divisible by Frame::kNumSlots.
- std::uint16_t height =
- display_h / num_stripes +
- (i + 1 == num_stripes ? display_h % num_stripes : 0);
- const auto* raw_start = &raw_screen[y * display_w * display_bpp];
- const auto* raw_end = raw_start + (height * display_w * display_bpp);
+ std::uint16_t height = frame_h / num_stripes +
+ (i + 1 == num_stripes ? frame_h % num_stripes : 0);
+ const auto* raw_start = &raw_screen[y * frame_w * frame_bpp];
+ const auto* raw_end = raw_start + (height * frame_w * frame_bpp);
// creating a named object and setting individual data members in order
// to make klp happy
// TODO (haining) construct this inside the call when not compiling
@@ -117,8 +113,8 @@
s.index = i;
s.x = 0;
s.y = y;
- s.width = display_w;
- s.stride = display_stride_bytes;
+ s.width = frame_w;
+ s.stride = frame_stride_bytes;
s.height = height;
s.frame_id = next_frame_number++;
s.raw_data.assign(raw_start, raw_end);
@@ -137,12 +133,11 @@
* callback should be set before the first WaitForAtLeastOneClientConnection()
* (b/178504150) and the first OnFrameAfter().
*/
- if (!screen_connector_->IsCallbackSet()) {
+ if (!screen_connector_.IsCallbackSet()) {
LOG(FATAL) << "ScreenConnector callback hasn't been set before MakeStripes";
}
while (!closed()) {
- bb_->WaitForAtLeastOneClientConnection();
- auto sim_hw_processed_frame = screen_connector_->OnNextFrame();
+ auto sim_hw_processed_frame = screen_connector_.OnNextFrame();
// sim_hw_processed_frame has display number from the guest
if (!sim_hw_processed_frame.is_success_) {
continue;
@@ -174,5 +169,5 @@
int SimulatedHWComposer::NumberOfStripes() { return kNumStripes; }
void SimulatedHWComposer::ReportClientsConnected() {
- screen_connector_->ReportClientsConnected(true);
+ screen_connector_.ReportClientsConnected(true);
}
diff --git a/host/frontend/vnc_server/simulated_hw_composer.h b/host/frontend/vnc_server/simulated_hw_composer.h
index fadb4a1..ff7e9dd 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.h
+++ b/host/frontend/vnc_server/simulated_hw_composer.h
@@ -26,33 +26,17 @@
#include "common/libs/concurrency/thread_annotations.h"
#include "common/libs/concurrency/thread_safe_queue.h"
-#include "host/frontend/vnc_server/blackboard.h"
#include "host/frontend/vnc_server/vnc_utils.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/screen_connector/screen_connector.h"
namespace cuttlefish {
namespace vnc {
-/**
- * ScreenConnectorImpl will generate this, and enqueue
- *
- * It's basically a (processed) frame, so it:
- * must be efficiently std::move-able
- * Also, for the sake of algorithm simplicity:
- * must be default-constructable & assignable
- *
- */
-struct VncScProcessedFrame : public ScreenConnectorFrameInfo {
- Message raw_screen_;
- std::deque<Stripe> stripes_;
-};
-
class SimulatedHWComposer {
public:
- using ScreenConnector = ScreenConnector<VncScProcessedFrame>;
using GenerateProcessedFrameCallback = ScreenConnector::GenerateProcessedFrameCallback;
- SimulatedHWComposer(BlackBoard* bb);
+ SimulatedHWComposer(ScreenConnector& screen_connector);
SimulatedHWComposer(const SimulatedHWComposer&) = delete;
SimulatedHWComposer& operator=(const SimulatedHWComposer&) = delete;
~SimulatedHWComposer();
@@ -80,10 +64,9 @@
constexpr static std::size_t kMaxQueueElements = 64;
bool closed_ GUARDED_BY(m_){};
std::mutex m_;
- BlackBoard* bb_{};
ThreadSafeQueue<Stripe> stripes_;
std::thread stripe_maker_;
- std::unique_ptr<ScreenConnector> screen_connector_;
+ ScreenConnector& screen_connector_;
};
} // namespace vnc
} // namespace cuttlefish
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
index 3143a6d..4876338 100644
--- a/host/frontend/vnc_server/virtual_inputs.cpp
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -15,8 +15,10 @@
*/
#include "host/frontend/vnc_server/virtual_inputs.h"
-#include <gflags/gflags.h>
+
#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <gflags/gflags.h>
#include <linux/input.h>
#include <linux/uinput.h>
@@ -25,13 +27,15 @@
#include <thread>
#include "keysyms.h"
-#include <common/libs/fs/shared_select.h>
-#include <host/libs/config/cuttlefish_config.h>
+#include "common/libs/confui/confui.h"
+#include "common/libs/fs/shared_select.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/logging.h"
using cuttlefish::vnc::VirtualInputs;
-DEFINE_int32(touch_fd, -1,
- "A fd for a socket where to accept touch connections");
+DEFINE_string(touch_fds, "",
+ "A list of fds for sockets where to accept touch connections");
DEFINE_int32(keyboard_fd, -1,
"A fd for a socket where to accept keyboard connections");
@@ -312,9 +316,10 @@
}
void ClientConnectorLoop() {
- auto touch_server = cuttlefish::SharedFD::Dup(FLAGS_touch_fd);
- close(FLAGS_touch_fd);
- FLAGS_touch_fd = -1;
+ auto touch_fd =
+ std::stoi(android::base::Split(FLAGS_touch_fds, ",").front());
+ auto touch_server = cuttlefish::SharedFD::Dup(touch_fd);
+ close(touch_fd);
auto keyboard_server = cuttlefish::SharedFD::Dup(FLAGS_keyboard_fd);
close(FLAGS_keyboard_fd);
@@ -347,6 +352,61 @@
VirtualInputs::VirtualInputs() { AddKeyMappings(&keymapping_); }
-VirtualInputs* VirtualInputs::Get() {
- return new SocketVirtualInputs();
+/**
+ * Depending on the host mode (e.g. android, confirmation ui(tee), etc)
+ * deliver the inputs to the right input implementation
+ * e.g. ConfUI's input or regular socket based input
+ */
+class VirtualInputDemux : public VirtualInputs {
+ public:
+ VirtualInputDemux(cuttlefish::confui::HostVirtualInput& confui_input)
+ : confui_input_{confui_input} {}
+ virtual ~VirtualInputDemux() = default;
+
+ virtual void GenerateKeyPressEvent(int code, bool down) override;
+ virtual void PressPowerButton(bool down) override;
+ virtual void HandlePointerEvent(bool touch_down, int x, int y) override;
+
+ private:
+ SocketVirtualInputs socket_virtual_input_;
+ cuttlefish::confui::HostVirtualInput& confui_input_;
+};
+
+void VirtualInputDemux::GenerateKeyPressEvent(int code, bool down) {
+ // confui input is active only in the confirmation UI
+ // also, socket virtual input should be inactive in the confirmation
+ // UI session
+ if (confui_input_.IsConfUiActive()) {
+ if (code == cuttlefish::xk::Menu) {
+ // release menu button in confirmation UI means for now cancel
+ confui_input_.PressCancelButton(down);
+ }
+ ConfUiLog(DEBUG) << "the key" << code << "ignored."
+ << "currently confirmation UI handles"
+ << "menu and power only.";
+ return;
+ }
+ socket_virtual_input_.GenerateKeyPressEvent(code, down);
+}
+
+void VirtualInputDemux::PressPowerButton(bool down) {
+ if (confui_input_.IsConfUiActive()) {
+ confui_input_.PressConfirmButton(down);
+ return;
+ }
+ socket_virtual_input_.PressPowerButton(down);
+}
+
+void VirtualInputDemux::HandlePointerEvent(bool touch_down, int x, int y) {
+ if (confui_input_.IsConfUiActive()) {
+ ConfUiLog(DEBUG) << "currently confirmation UI ignores pointer events at ("
+ << x << ", " << y << ")";
+ return;
+ }
+ socket_virtual_input_.HandlePointerEvent(touch_down, x, y);
+}
+
+std::shared_ptr<VirtualInputs> VirtualInputs::Get(
+ cuttlefish::confui::HostVirtualInput& confui_input) {
+ return std::make_shared<VirtualInputDemux>(confui_input);
}
diff --git a/host/frontend/vnc_server/virtual_inputs.h b/host/frontend/vnc_server/virtual_inputs.h
index c22de15..f30202e 100644
--- a/host/frontend/vnc_server/virtual_inputs.h
+++ b/host/frontend/vnc_server/virtual_inputs.h
@@ -16,17 +16,20 @@
* limitations under the License.
*/
-#include "vnc_utils.h"
-
#include <map>
+#include <memory>
#include <mutex>
+#include "host/libs/confui/host_virtual_input.h"
+#include "vnc_utils.h"
+
namespace cuttlefish {
namespace vnc {
class VirtualInputs {
public:
- static VirtualInputs* Get();
+ static std::shared_ptr<VirtualInputs> Get(
+ cuttlefish::confui::HostVirtualInput& confui_input);
virtual ~VirtualInputs() = default;
diff --git a/host/frontend/vnc_server/vnc_server.cpp b/host/frontend/vnc_server/vnc_server.cpp
index dd489b8..eff0d57 100644
--- a/host/frontend/vnc_server/vnc_server.cpp
+++ b/host/frontend/vnc_server/vnc_server.cpp
@@ -16,6 +16,8 @@
#include "host/frontend/vnc_server/vnc_server.h"
+#include <memory>
+
#include <android-base/logging.h>
#include "common/libs/utils/tcp_socket.h"
#include "host/frontend/vnc_server/blackboard.h"
@@ -27,11 +29,13 @@
using cuttlefish::vnc::VncServer;
-VncServer::VncServer(int port, bool aggressive)
+VncServer::VncServer(int port, bool aggressive,
+ cuttlefish::vnc::ScreenConnector& screen_connector,
+ cuttlefish::confui::HostVirtualInput& confui_input)
: server_(port),
- virtual_inputs_(VirtualInputs::Get()),
- frame_buffer_watcher_{&bb_},
- aggressive_{aggressive} {}
+ virtual_inputs_(VirtualInputs::Get(confui_input)),
+ frame_buffer_watcher_{&bb_, screen_connector},
+ aggressive_{aggressive} {}
void VncServer::MainLoop() {
while (true) {
diff --git a/host/frontend/vnc_server/vnc_server.h b/host/frontend/vnc_server/vnc_server.h
index 3ae37d8..2751fd1 100644
--- a/host/frontend/vnc_server/vnc_server.h
+++ b/host/frontend/vnc_server/vnc_server.h
@@ -28,13 +28,18 @@
#include "host/frontend/vnc_server/virtual_inputs.h"
#include "host/frontend/vnc_server/vnc_client_connection.h"
#include "host/frontend/vnc_server/vnc_utils.h"
+#include "host/libs/confui/host_mode_ctrl.h"
+#include "host/libs/confui/host_virtual_input.h"
+#include "host/libs/screen_connector/screen_connector.h"
namespace cuttlefish {
namespace vnc {
class VncServer {
public:
- explicit VncServer(int port, bool aggressive);
+ explicit VncServer(int port, bool aggressive,
+ ScreenConnector& screen_connector,
+ cuttlefish::confui::HostVirtualInput& confui_input);
VncServer(const VncServer&) = delete;
VncServer& operator=(const VncServer&) = delete;
@@ -47,8 +52,10 @@
void StartClientThread(ClientSocket sock);
ServerSocket server_;
+
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 34df434..7ec19f7 100644
--- a/host/frontend/vnc_server/vnc_utils.h
+++ b/host/frontend/vnc_server/vnc_utils.h
@@ -18,12 +18,14 @@
#include <array>
#include <cstdint>
+#include <memory>
#include <utility>
#include <vector>
#include "common/libs/utils/size_utils.h"
#include "common/libs/utils/tcp_socket.h"
#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/screen_connector/screen_connector.h"
namespace cuttlefish {
namespace vnc {
@@ -63,5 +65,26 @@
ScreenOrientation orientation{};
};
+/**
+ * ScreenConnectorImpl will generate this, and enqueue
+ *
+ * It's basically a (processed) frame, so it:
+ * must be efficiently std::move-able
+ * Also, for the sake of algorithm simplicity:
+ * must be default-constructable & assignable
+ *
+ */
+struct VncScProcessedFrame : public ScreenConnectorFrameInfo {
+ Message raw_screen_;
+ std::deque<Stripe> stripes_;
+ std::unique_ptr<VncScProcessedFrame> Clone() {
+ VncScProcessedFrame* cloned_frame = new VncScProcessedFrame();
+ cloned_frame->raw_screen_ = raw_screen_;
+ cloned_frame->stripes_ = stripes_;
+ return std::unique_ptr<VncScProcessedFrame>(cloned_frame);
+ }
+};
+using ScreenConnector = cuttlefish::ScreenConnector<VncScProcessedFrame>;
+
} // namespace vnc
} // namespace cuttlefish
diff --git a/host/frontend/webrtc/Android.bp b/host/frontend/webrtc/Android.bp
index e0890ad..42f7fa4 100644
--- a/host/frontend/webrtc/Android.bp
+++ b/host/frontend/webrtc/Android.bp
@@ -22,6 +22,7 @@
srcs: [
"lib/audio_device.cpp",
"lib/audio_track_source_impl.cpp",
+ "lib/camera_streamer.cpp",
"lib/client_handler.cpp",
"lib/keyboard.cpp",
"lib/local_recorder.cpp",
@@ -50,8 +51,9 @@
"libgflags",
"libdrm",
"libffi",
- "libwayland_server",
+ "libwayland_crosvm_gpu_display_extension_server_protocols",
"libwayland_extension_server_protocols",
+ "libwayland_server",
"libwebsockets",
"libcap",
"libcuttlefish_utils",
@@ -74,6 +76,7 @@
srcs: [
"adb_handler.cpp",
"audio_handler.cpp",
+ "bluetooth_handler.cpp",
"connection_observer.cpp",
"cvd_video_frame_buffer.cpp",
"display_handler.cpp",
@@ -83,6 +86,7 @@
header_libs: [
"webrtc_signaling_headers",
"libwebrtc_absl_headers",
+ "libcuttlefish_confui_host_headers",
],
static_libs: [
"libwebrtc_absl_base",
@@ -103,6 +107,11 @@
"libcuttlefish_screen_connector",
"libcuttlefish_utils",
"libcuttlefish_wayland_server",
+ "libcuttlefish_confui",
+ "libcuttlefish_confui_host",
+ "libft2.nodep",
+ "libteeui",
+ "libteeui_localization",
"libdrm",
"libevent",
"libffi",
@@ -110,6 +119,7 @@
"libopus",
"libsrtp2",
"libvpx",
+ "libwayland_crosvm_gpu_display_extension_server_protocols",
"libwayland_extension_server_protocols",
"libwayland_server",
"libwebrtc",
diff --git a/host/frontend/webrtc/adb_handler.cpp b/host/frontend/webrtc/adb_handler.cpp
index ae2eccf..7ff55fb 100644
--- a/host/frontend/webrtc/adb_handler.cpp
+++ b/host/frontend/webrtc/adb_handler.cpp
@@ -43,7 +43,13 @@
return SharedFD();
}
- return SharedFD::SocketLocalClient(port, SOCK_STREAM);
+ auto local_client = SharedFD::SocketLocalClient(port, SOCK_STREAM);
+ if (!local_client->IsOpen()) {
+ LOG(WARNING) << "Failed to connect to ADB server socket (non-Android guest?) Using /dev/null workaround."
+ << local_client->StrError();
+ return SharedFD::Open("/dev/null", O_RDWR);
+ }
+ return local_client;
}
} // namespace
diff --git a/host/frontend/webrtc/audio_handler.cpp b/host/frontend/webrtc/audio_handler.cpp
index 4046995..1cd8938 100644
--- a/host/frontend/webrtc/audio_handler.cpp
+++ b/host/frontend/webrtc/audio_handler.cpp
@@ -25,6 +25,28 @@
namespace cuttlefish {
namespace {
+const virtio_snd_jack_info JACKS[] = {};
+constexpr uint32_t NUM_JACKS = sizeof(JACKS) / sizeof(JACKS[0]);
+
+const virtio_snd_chmap_info CHMAPS[] = {{
+ .hdr = { .hda_fn_nid = Le32(0), },
+ .direction = (uint8_t) AudioStreamDirection::VIRTIO_SND_D_OUTPUT,
+ .channels = 2,
+ .positions = {
+ (uint8_t) AudioChannelMap::VIRTIO_SND_CHMAP_FL,
+ (uint8_t) AudioChannelMap::VIRTIO_SND_CHMAP_FR
+ },
+}, {
+ .hdr = { .hda_fn_nid = Le32(0), },
+ .direction = (uint8_t) AudioStreamDirection::VIRTIO_SND_D_INPUT,
+ .channels = 2,
+ .positions = {
+ (uint8_t) AudioChannelMap::VIRTIO_SND_CHMAP_FL,
+ (uint8_t) AudioChannelMap::VIRTIO_SND_CHMAP_FR
+ },
+}};
+constexpr uint32_t NUM_CHMAPS = sizeof(CHMAPS) / sizeof(CHMAPS[0]);
+
const virtio_snd_pcm_info STREAMS[] = {{
.hdr =
{
@@ -35,10 +57,10 @@
// formats: It only takes the bits_per_sample as a parameter and assumes
// the underlying format to be one of the following:
.formats = Le64(
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U8) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U16) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U24) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U32)),
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S8) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S16) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S24) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S32)),
.rates = Le64(
(((uint64_t)1) << (uint8_t)AudioStreamRate::VIRTIO_SND_PCM_RATE_5512) |
(((uint64_t)1) << (uint8_t)AudioStreamRate::VIRTIO_SND_PCM_RATE_8000) |
@@ -70,10 +92,10 @@
// formats: It only takes the bits_per_sample as a parameter and assumes
// the underlying format to be one of the following:
.formats = Le64(
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U8) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U16) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U24) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U32)),
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S8) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S16) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S24) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S32)),
.rates = Le64(
(((uint64_t)1) << (uint8_t)AudioStreamRate::VIRTIO_SND_PCM_RATE_5512) |
(((uint64_t)1) << (uint8_t)AudioStreamRate::VIRTIO_SND_PCM_RATE_8000) |
@@ -271,7 +293,7 @@
[[noreturn]] void AudioHandler::Loop() {
for (;;) {
auto audio_client = audio_server_->AcceptClient(
- NUM_STREAMS, 0 /* num_jacks, */, 0 /* num_chmaps, */,
+ NUM_STREAMS, NUM_JACKS, NUM_CHMAPS,
262144 /* tx_shm_len */, 262144 /* rx_shm_len */);
CHECK(audio_client) << "Failed to create audio client connection instance";
@@ -362,6 +384,28 @@
cmd.Reply(AudioStatus::VIRTIO_SND_S_OK);
}
+void AudioHandler::ChmapsInfo(ChmapInfoCommand& cmd) {
+ if (cmd.start_id() >= NUM_CHMAPS ||
+ cmd.start_id() + cmd.count() > NUM_CHMAPS) {
+ cmd.Reply(AudioStatus::VIRTIO_SND_S_BAD_MSG, {});
+ return;
+ }
+ std::vector<virtio_snd_chmap_info> chmap_info(
+ &CHMAPS[cmd.start_id()], &CHMAPS[cmd.start_id()] + cmd.count());
+ cmd.Reply(AudioStatus::VIRTIO_SND_S_OK, chmap_info);
+}
+
+void AudioHandler::JacksInfo(JackInfoCommand& cmd) {
+ if (cmd.start_id() >= NUM_JACKS ||
+ cmd.start_id() + cmd.count() > NUM_JACKS) {
+ cmd.Reply(AudioStatus::VIRTIO_SND_S_BAD_MSG, {});
+ return;
+ }
+ std::vector<virtio_snd_jack_info> jack_info(
+ &JACKS[cmd.start_id()], &JACKS[cmd.start_id()] + cmd.count());
+ cmd.Reply(AudioStatus::VIRTIO_SND_S_OK, jack_info);
+}
+
void AudioHandler::OnPlaybackBuffer(TxBuffer buffer) {
auto stream_id = buffer.stream_id();
auto& stream_desc = stream_descs_[stream_id];
diff --git a/host/frontend/webrtc/audio_handler.h b/host/frontend/webrtc/audio_handler.h
index 3802052..4da645c 100644
--- a/host/frontend/webrtc/audio_handler.h
+++ b/host/frontend/webrtc/audio_handler.h
@@ -63,6 +63,8 @@
void ReleaseStream(StreamControlCommand& cmd) override;
void StartStream(StreamControlCommand& cmd) override;
void StopStream(StreamControlCommand& cmd) override;
+ void ChmapsInfo(ChmapInfoCommand& cmd) override;
+ void JacksInfo(JackInfoCommand& cmd) override;
void OnPlaybackBuffer(TxBuffer buffer) override;
void OnCaptureBuffer(RxBuffer buffer) override;
diff --git a/host/frontend/webrtc/bluetooth_handler.cpp b/host/frontend/webrtc/bluetooth_handler.cpp
new file mode 100644
index 0000000..219c36e
--- /dev/null
+++ b/host/frontend/webrtc/bluetooth_handler.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 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/webrtc/bluetooth_handler.h"
+
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+using namespace android;
+
+namespace cuttlefish {
+namespace webrtc_streaming {
+
+BluetoothHandler::BluetoothHandler(
+ const int rootCanalTestPort,
+ std::function<void(const uint8_t *, size_t)> send_to_client)
+ : send_to_client_(send_to_client),
+ rootcanal_socket_(
+ SharedFD::SocketLocalClient(rootCanalTestPort, SOCK_STREAM)),
+ shutdown_(SharedFD::Event(0, 0)) {
+ std::thread loop([this]() { ReadLoop(); });
+ read_thread_.swap(loop);
+}
+
+BluetoothHandler::~BluetoothHandler() {
+ // Send a message to the looper to shut down.
+ uint64_t v = 1;
+ shutdown_->Write(&v, sizeof(v));
+ // Shut down the socket as well. Not strictly necessary.
+ rootcanal_socket_->Shutdown(SHUT_RDWR);
+ read_thread_.join();
+}
+
+void BluetoothHandler::ReadLoop() {
+ while (1) {
+ uint8_t buffer[4096];
+
+ read_set_.Set(shutdown_);
+ read_set_.Set(rootcanal_socket_);
+ Select(&read_set_, nullptr, nullptr, nullptr);
+
+ if (read_set_.IsSet(rootcanal_socket_)) {
+ auto read = rootcanal_socket_->Read(buffer, sizeof(buffer));
+ if (read < 0) {
+ PLOG(ERROR) << "Error on reading from RootCanal socket.";
+ break;
+ }
+ if (read) {
+ send_to_client_(buffer, read);
+ }
+ }
+
+ if (read_set_.IsSet(shutdown_)) {
+ LOG(INFO) << "BluetoothHandler is shutting down.";
+ break;
+ }
+ }
+}
+
+void BluetoothHandler::handleMessage(const uint8_t *msg, size_t len) {
+ size_t sent = 0;
+ while (sent < len) {
+ auto this_sent = rootcanal_socket_->Write(&msg[sent], len - sent);
+ if (this_sent < 0) {
+ PLOG(FATAL) << "Error writing to rootcanal socket.";
+ return;
+ }
+ sent += this_sent;
+ }
+}
+
+} // namespace webrtc_streaming
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/bluetooth_handler.h b/host/frontend/webrtc/bluetooth_handler.h
new file mode 100644
index 0000000..72248cf
--- /dev/null
+++ b/host/frontend/webrtc/bluetooth_handler.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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 <thread>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+
+namespace cuttlefish {
+namespace webrtc_streaming {
+
+struct BluetoothHandler {
+ explicit BluetoothHandler(
+ const int rootCanalTestPort,
+ std::function<void(const uint8_t *, size_t)> send_to_client);
+
+ ~BluetoothHandler();
+
+ void handleMessage(const uint8_t *msg, size_t len);
+
+ private:
+ std::function<void(const uint8_t *, size_t)> send_to_client_;
+
+ void ReadLoop();
+
+ SharedFD rootcanal_socket_;
+ SharedFD shutdown_;
+ SharedFDSet read_set_;
+ std::thread read_thread_;
+};
+
+} // namespace webrtc_streaming
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/connection_observer.cpp b/host/frontend/webrtc/connection_observer.cpp
index 3be8fa4..21c1312 100644
--- a/host/frontend/webrtc/connection_observer.cpp
+++ b/host/frontend/webrtc/connection_observer.cpp
@@ -20,6 +20,7 @@
#include <linux/input.h>
+#include <chrono>
#include <map>
#include <set>
#include <thread>
@@ -30,9 +31,11 @@
#include <android-base/logging.h>
#include <gflags/gflags.h>
+#include "common/libs/confui/confui.h"
#include "common/libs/fs/shared_buf.h"
#include "host/frontend/webrtc/adb_handler.h"
-#include "host/frontend/webrtc/kernel_log_events_handler.h"
+#include "host/frontend/webrtc/bluetooth_handler.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
#include "host/frontend/webrtc/lib/utils.h"
#include "host/libs/config/cuttlefish_config.h"
@@ -88,23 +91,36 @@
}
}
+/**
+ * connection observer implementation for regular android mode.
+ * i.e. when it is not in the confirmation UI mode (or TEE),
+ * the control flow will fall back to this ConnectionObserverForAndroid
+ */
class ConnectionObserverImpl
: public cuttlefish::webrtc_streaming::ConnectionObserver {
public:
- ConnectionObserverImpl(cuttlefish::InputSockets& input_sockets,
- cuttlefish::SharedFD kernel_log_events_fd,
- std::map<std::string, cuttlefish::SharedFD>
- commands_to_custom_action_servers,
- std::weak_ptr<DisplayHandler> display_handler)
+ ConnectionObserverImpl(
+ cuttlefish::InputSockets &input_sockets,
+ cuttlefish::KernelLogEventsHandler *kernel_log_events_handler,
+ std::map<std::string, cuttlefish::SharedFD>
+ commands_to_custom_action_servers,
+ std::weak_ptr<DisplayHandler> display_handler,
+ CameraController *camera_controller,
+ cuttlefish::confui::HostVirtualInput &confui_input)
: input_sockets_(input_sockets),
- kernel_log_events_client_(kernel_log_events_fd),
+ kernel_log_events_handler_(kernel_log_events_handler),
commands_to_custom_action_servers_(commands_to_custom_action_servers),
- weak_display_handler_(display_handler) {}
+ weak_display_handler_(display_handler),
+ camera_controller_(camera_controller),
+ confui_input_(confui_input) {}
virtual ~ConnectionObserverImpl() {
auto display_handler = weak_display_handler_.lock();
if (display_handler) {
display_handler->DecClientCount();
}
+ if (kernel_log_subscription_id_ != -1) {
+ kernel_log_events_handler_->Unsubscribe(kernel_log_subscription_id_);
+ }
}
void OnConnected(std::function<void(const uint8_t *, size_t, bool)>
@@ -112,16 +128,30 @@
auto display_handler = weak_display_handler_.lock();
if (display_handler) {
display_handler->IncClientCount();
- // A long time may pass before the next frame comes up from the guest.
- // Send the last one to avoid showing a black screen to the user during
- // that time.
- display_handler->SendLastFrame();
+ std::thread th([this]() {
+ // The encoder in libwebrtc won't drop 5 consecutive frames due to frame
+ // size, so we make sure at least 5 frames are sent every time a client
+ // connects to ensure they receive at least one.
+ constexpr int kNumFrames = 5;
+ constexpr int kMillisPerFrame = 16;
+ for (int i = 0; i < kNumFrames; ++i) {
+ auto display_handler = weak_display_handler_.lock();
+ display_handler->SendLastFrame();
+ if (i < kNumFrames - 1) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(kMillisPerFrame));
+ }
+ }
+ });
+ th.detach();
}
}
- void OnTouchEvent(const std::string & /*display_label*/, int x, int y,
+ void OnTouchEvent(const std::string &display_label, int x, int y,
bool down) override {
-
+ if (confui_input_.IsConfUiActive()) {
+ ConfUiLog(DEBUG) << "touch event ignored in confirmation UI mode";
+ return;
+ }
auto buffer = GetEventBuffer();
if (!buffer) {
LOG(ERROR) << "Failed to allocate event buffer";
@@ -131,22 +161,25 @@
buffer->AddEvent(EV_ABS, ABS_Y, y);
buffer->AddEvent(EV_KEY, BTN_TOUCH, down);
buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
- cuttlefish::WriteAll(input_sockets_.touch_client,
+ cuttlefish::WriteAll(input_sockets_.GetTouchClientByLabel(display_label),
reinterpret_cast<const char *>(buffer->data()),
buffer->size());
}
- void OnMultiTouchEvent(const std::string & /*display_label*/, Json::Value id,
+ void OnMultiTouchEvent(const std::string &display_label, Json::Value id,
Json::Value slot, Json::Value x, Json::Value y,
- bool down, int size) override {
-
+ bool down, int size) {
+ if (confui_input_.IsConfUiActive()) {
+ ConfUiLog(DEBUG) << "multi-touch event ignored in confirmation UI mode";
+ return;
+ }
auto buffer = GetEventBuffer();
if (!buffer) {
LOG(ERROR) << "Failed to allocate event buffer";
return;
}
- for (int i=0; i<size; i++) {
+ for (int i = 0; i < size; i++) {
auto this_slot = slot[i].asInt();
auto this_id = id[i].asInt();
auto this_x = x[i].asInt();
@@ -176,12 +209,30 @@
}
buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
- cuttlefish::WriteAll(input_sockets_.touch_client,
+ cuttlefish::WriteAll(input_sockets_.GetTouchClientByLabel(display_label),
reinterpret_cast<const char *>(buffer->data()),
buffer->size());
}
void OnKeyboardEvent(uint16_t code, bool down) override {
+ if (confui_input_.IsConfUiActive()) {
+ ConfUiLog(DEBUG) << "and it's confirmation UI mode";
+ switch (code) {
+ case KEY_POWER:
+ ConfUiLog(DEBUG) << "Power pushed mode";
+ confui_input_.PressConfirmButton(down);
+ break;
+ case KEY_MENU:
+ confui_input_.PressCancelButton(down);
+ break;
+ default:
+ ConfUiLog(DEBUG) << "key" << code
+ << "is ignored in confirmation UI mode";
+ break;
+ }
+ return;
+ }
+
auto buffer = GetEventBuffer();
if (!buffer) {
LOG(ERROR) << "Failed to allocate event buffer";
@@ -222,9 +273,11 @@
void OnControlChannelOpen(std::function<bool(const Json::Value)>
control_message_sender) override {
LOG(VERBOSE) << "Control Channel open";
- kernel_log_events_handler_.reset(new cuttlefish::webrtc_streaming::KernelLogEventsHandler(
- kernel_log_events_client_,
- control_message_sender));
+ if (camera_controller_) {
+ camera_controller_->SetMessageSender(control_message_sender);
+ }
+ kernel_log_subscription_id_ =
+ kernel_log_events_handler_->AddSubscriber(control_message_sender);
}
void OnControlMessage(const uint8_t* msg, size_t size) override {
Json::Value evt;
@@ -236,68 +289,118 @@
LOG(ERROR) << "Received invalid JSON object over control channel: " << errorMessage;
return;
}
- auto result =
- webrtc_streaming::ValidationResult::ValidateJsonObject(evt, "command",
- {{"command", Json::ValueType::stringValue},
- {"state", Json::ValueType::stringValue}});
+
+ auto result = webrtc_streaming::ValidationResult::ValidateJsonObject(
+ evt, "command",
+ /*required_fields=*/{{"command", Json::ValueType::stringValue}},
+ /*optional_fields=*/
+ {
+ {"button_state", Json::ValueType::stringValue},
+ {"lid_switch_open", Json::ValueType::booleanValue},
+ {"hinge_angle_value", Json::ValueType::intValue},
+ });
if (!result.ok()) {
LOG(ERROR) << result.error();
return;
}
auto command = evt["command"].asString();
- auto state = evt["state"].asString();
- LOG(VERBOSE) << "Control command: " << command << " (" << state << ")";
+ if (command == "device_state") {
+ if (evt.isMember("lid_switch_open")) {
+ // InputManagerService treats a value of 0 as open and 1 as closed, so
+ // invert the lid_switch_open value that is sent to the input device.
+ OnSwitchEvent(SW_LID, !evt["lid_switch_open"].asBool());
+ }
+ if (evt.isMember("hinge_angle_value")) {
+ // TODO(b/181157794) Propagate hinge angle sensor data using a custom
+ // Sensor HAL.
+ }
+ return;
+ } else if (command.rfind("camera_", 0) == 0 && camera_controller_) {
+ // Handle commands starting with "camera_" by camera controller
+ camera_controller_->HandleMessage(evt);
+ return;
+ }
+
+ auto button_state = evt["button_state"].asString();
+ LOG(VERBOSE) << "Control command: " << command << " (" << button_state
+ << ")";
if (command == "power") {
- OnKeyboardEvent(KEY_POWER, state == "down");
+ OnKeyboardEvent(KEY_POWER, button_state == "down");
} else if (command == "home") {
- OnKeyboardEvent(KEY_HOMEPAGE, state == "down");
+ OnKeyboardEvent(KEY_HOMEPAGE, button_state == "down");
} else if (command == "menu") {
- OnKeyboardEvent(KEY_MENU, state == "down");
+ OnKeyboardEvent(KEY_MENU, button_state == "down");
} else if (command == "volumemute") {
- OnKeyboardEvent(KEY_MUTE, state == "down");
+ OnKeyboardEvent(KEY_MUTE, button_state == "down");
} else if (command == "volumedown") {
- OnKeyboardEvent(KEY_VOLUMEDOWN, state == "down");
+ OnKeyboardEvent(KEY_VOLUMEDOWN, button_state == "down");
} else if (command == "volumeup") {
- OnKeyboardEvent(KEY_VOLUMEUP, state == "down");
+ OnKeyboardEvent(KEY_VOLUMEUP, button_state == "down");
} else if (commands_to_custom_action_servers_.find(command) !=
commands_to_custom_action_servers_.end()) {
// Simple protocol for commands forwarded to action servers:
// - Always 128 bytes
- // - Format: command:state
+ // - Format: command:button_state
// - Example: my_button:down
- std::string action_server_message = command + ":" + state;
+ std::string action_server_message = command + ":" + button_state;
cuttlefish::WriteAll(commands_to_custom_action_servers_[command],
action_server_message.c_str(), 128);
} else {
- LOG(WARNING) << "Unsupported control command: " << command << " (" << state << ")";
- // TODO(b/163081337): Handle custom commands.
+ LOG(WARNING) << "Unsupported control command: " << command << " ("
+ << button_state << ")";
+ }
+ }
+
+ void OnBluetoothChannelOpen(std::function<bool(const uint8_t *, size_t)>
+ bluetooth_message_sender) override {
+ LOG(VERBOSE) << "Bluetooth channel open";
+ bluetooth_handler_.reset(new cuttlefish::webrtc_streaming::BluetoothHandler(
+ cuttlefish::CuttlefishConfig::Get()
+ ->ForDefaultInstance()
+ .rootcanal_test_port(),
+ bluetooth_message_sender));
+ }
+
+ void OnBluetoothMessage(const uint8_t *msg, size_t size) override {
+ bluetooth_handler_->handleMessage(msg, size);
+ }
+
+ void OnCameraData(const std::vector<char> &data) override {
+ if (camera_controller_) {
+ camera_controller_->HandleMessage(data);
}
}
private:
cuttlefish::InputSockets& input_sockets_;
- cuttlefish::SharedFD kernel_log_events_client_;
+ cuttlefish::KernelLogEventsHandler* kernel_log_events_handler_;
+ int kernel_log_subscription_id_ = -1;
std::shared_ptr<cuttlefish::webrtc_streaming::AdbHandler> adb_handler_;
- std::shared_ptr<cuttlefish::webrtc_streaming::KernelLogEventsHandler> kernel_log_events_handler_;
+ std::shared_ptr<cuttlefish::webrtc_streaming::BluetoothHandler>
+ bluetooth_handler_;
std::map<std::string, cuttlefish::SharedFD> commands_to_custom_action_servers_;
std::weak_ptr<DisplayHandler> weak_display_handler_;
std::set<int32_t> active_touch_slots_;
+ cuttlefish::CameraController *camera_controller_;
+ cuttlefish::confui::HostVirtualInput &confui_input_;
};
CfConnectionObserverFactory::CfConnectionObserverFactory(
- cuttlefish::InputSockets& input_sockets,
- cuttlefish::SharedFD kernel_log_events_fd)
+ cuttlefish::InputSockets &input_sockets,
+ cuttlefish::KernelLogEventsHandler* kernel_log_events_handler,
+ cuttlefish::confui::HostVirtualInput &confui_input)
: input_sockets_(input_sockets),
- kernel_log_events_fd_(kernel_log_events_fd) {}
+ kernel_log_events_handler_(kernel_log_events_handler),
+ confui_input_{confui_input} {}
std::shared_ptr<cuttlefish::webrtc_streaming::ConnectionObserver>
CfConnectionObserverFactory::CreateObserver() {
return std::shared_ptr<cuttlefish::webrtc_streaming::ConnectionObserver>(
- new ConnectionObserverImpl(input_sockets_,
- kernel_log_events_fd_,
+ new ConnectionObserverImpl(input_sockets_, kernel_log_events_handler_,
commands_to_custom_action_servers_,
- weak_display_handler_));
+ weak_display_handler_, camera_controller_,
+ confui_input_));
}
void CfConnectionObserverFactory::AddCustomActionServer(
@@ -313,4 +416,9 @@
std::weak_ptr<DisplayHandler> display_handler) {
weak_display_handler_ = display_handler;
}
+
+void CfConnectionObserverFactory::SetCameraHandler(
+ CameraController *controller) {
+ camera_controller_ = controller;
+}
} // namespace cuttlefish
diff --git a/host/frontend/webrtc/connection_observer.h b/host/frontend/webrtc/connection_observer.h
index 9ec1e49..f31472c 100644
--- a/host/frontend/webrtc/connection_observer.h
+++ b/host/frontend/webrtc/connection_observer.h
@@ -21,40 +21,55 @@
#include "common/libs/fs/shared_fd.h"
#include "host/frontend/webrtc/display_handler.h"
+#include "host/frontend/webrtc/kernel_log_events_handler.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
#include "host/frontend/webrtc/lib/connection_observer.h"
+#include "host/libs/confui/host_virtual_input.h"
namespace cuttlefish {
struct InputSockets {
- cuttlefish::SharedFD touch_server;
- cuttlefish::SharedFD touch_client;
- cuttlefish::SharedFD keyboard_server;
- cuttlefish::SharedFD keyboard_client;
- cuttlefish::SharedFD switches_server;
- cuttlefish::SharedFD switches_client;
+ SharedFD GetTouchClientByLabel(const std::string& label) {
+ return touch_clients[label];
+ }
+
+ // TODO (b/186773052): Finding strings in a map for every input event may
+ // introduce unwanted latency.
+ std::map<std::string, SharedFD> touch_servers;
+ std::map<std::string, SharedFD> touch_clients;
+ SharedFD keyboard_server;
+ SharedFD keyboard_client;
+ SharedFD switches_server;
+ SharedFD switches_client;
};
class CfConnectionObserverFactory
- : public cuttlefish::webrtc_streaming::ConnectionObserverFactory {
+ : public webrtc_streaming::ConnectionObserverFactory {
public:
- CfConnectionObserverFactory(cuttlefish::InputSockets& input_sockets,
- cuttlefish::SharedFD kernel_log_events_fd);
+ CfConnectionObserverFactory(
+ cuttlefish::InputSockets& input_sockets,
+ KernelLogEventsHandler* kernel_log_events_handler,
+ cuttlefish::confui::HostVirtualInput& confui_input);
~CfConnectionObserverFactory() override = default;
- std::shared_ptr<cuttlefish::webrtc_streaming::ConnectionObserver> CreateObserver()
+ std::shared_ptr<webrtc_streaming::ConnectionObserver> CreateObserver()
override;
- void AddCustomActionServer(cuttlefish::SharedFD custom_action_server_fd,
+ void AddCustomActionServer(SharedFD custom_action_server_fd,
const std::vector<std::string>& commands);
void SetDisplayHandler(std::weak_ptr<DisplayHandler> display_handler);
+ void SetCameraHandler(CameraController* controller);
+
private:
- cuttlefish::InputSockets& input_sockets_;
- cuttlefish::SharedFD kernel_log_events_fd_;
- std::map<std::string, cuttlefish::SharedFD>
+ InputSockets& input_sockets_;
+ KernelLogEventsHandler* kernel_log_events_handler_;
+ std::map<std::string, SharedFD>
commands_to_custom_action_servers_;
std::weak_ptr<DisplayHandler> weak_display_handler_;
+ cuttlefish::confui::HostVirtualInput& confui_input_;
+ cuttlefish::CameraController* camera_controller_ = nullptr;
};
} // namespace cuttlefish
diff --git a/host/frontend/webrtc/cvd_video_frame_buffer.cpp b/host/frontend/webrtc/cvd_video_frame_buffer.cpp
index 1582613..8d0aeea 100644
--- a/host/frontend/webrtc/cvd_video_frame_buffer.cpp
+++ b/host/frontend/webrtc/cvd_video_frame_buffer.cpp
@@ -16,6 +16,9 @@
#include "host/frontend/webrtc/cvd_video_frame_buffer.h"
+#include <map>
+#include <mutex>
+#include <vector>
#include "common/libs/utils/size_utils.h"
namespace cuttlefish {
@@ -28,14 +31,42 @@
return AlignToPowerOf2(width, kLogAlignment);
}
+std::multimap<int, std::vector<uint8_t>> pool;
+std::mutex pool_mutex;
+std::vector<uint8_t> FromPool(int size) {
+ {
+ std::lock_guard<std::mutex> lock(pool_mutex);
+ auto it = pool.find(size);
+ if (it != pool.end()) {
+ auto ret = std::move(it->second);
+ pool.erase(it);
+ return ret;
+ }
+ }
+ return std::vector<uint8_t>(size);
+}
+
+void BackToPool(std::vector<uint8_t> item) {
+ std::lock_guard<std::mutex> lock(pool_mutex);
+ pool.insert({item.size(), std::move(item)});
+}
+
} // namespace
CvdVideoFrameBuffer::CvdVideoFrameBuffer(int width, int height)
: width_(width),
height_(height),
- y_(AlignStride(width) * height + kPlanePadding),
- u_(AlignStride((width + 1) / 2) * ((height + 1) / 2) + kPlanePadding),
- v_(AlignStride((width + 1) / 2) * ((height + 1) / 2) + kPlanePadding) {}
+ y_(FromPool(AlignStride(width) * height + kPlanePadding)),
+ u_(FromPool(AlignStride((width + 1) / 2) * ((height + 1) / 2) +
+ kPlanePadding)),
+ v_(FromPool(AlignStride((width + 1) / 2) * ((height + 1) / 2) +
+ kPlanePadding)) {}
+
+CvdVideoFrameBuffer::~CvdVideoFrameBuffer() {
+ BackToPool(std::move(y_));
+ BackToPool(std::move(u_));
+ BackToPool(std::move(v_));
+}
int CvdVideoFrameBuffer::width() const { return width_; }
int CvdVideoFrameBuffer::height() const { return height_; }
diff --git a/host/frontend/webrtc/cvd_video_frame_buffer.h b/host/frontend/webrtc/cvd_video_frame_buffer.h
index 70d493b..84b4946 100644
--- a/host/frontend/webrtc/cvd_video_frame_buffer.h
+++ b/host/frontend/webrtc/cvd_video_frame_buffer.h
@@ -31,7 +31,7 @@
CvdVideoFrameBuffer& operator=(const CvdVideoFrameBuffer& cvd_frame_buf) = default;
CvdVideoFrameBuffer() = delete;
- ~CvdVideoFrameBuffer() override = default;
+ ~CvdVideoFrameBuffer() override;
int width() const override;
int height() const override;
diff --git a/host/frontend/webrtc/display_handler.cpp b/host/frontend/webrtc/display_handler.cpp
index 2b4f574..334c35f 100644
--- a/host/frontend/webrtc/display_handler.cpp
+++ b/host/frontend/webrtc/display_handler.cpp
@@ -24,36 +24,27 @@
namespace cuttlefish {
DisplayHandler::DisplayHandler(
- std::shared_ptr<webrtc_streaming::VideoSink> display_sink,
- std::unique_ptr<ScreenConnector> screen_connector)
- : display_sink_(display_sink), screen_connector_(std::move(screen_connector)) {
- screen_connector_->SetCallback(std::move(GetScreenConnectorCallback()));
+ std::vector<std::shared_ptr<webrtc_streaming::VideoSink>> display_sinks,
+ ScreenConnector& screen_connector)
+ : display_sinks_(display_sinks), screen_connector_(screen_connector) {
+ screen_connector_.SetCallback(std::move(GetScreenConnectorCallback()));
}
DisplayHandler::GenerateProcessedFrameCallback DisplayHandler::GetScreenConnectorCallback() {
// only to tell the producer how to create a ProcessedFrame to cache into the queue
DisplayHandler::GenerateProcessedFrameCallback callback =
- [](std::uint32_t display_number, std::uint8_t* frame_pixels,
+ [](std::uint32_t display_number, std::uint32_t frame_width,
+ std::uint32_t frame_height, std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_pixels,
WebRtcScProcessedFrame& processed_frame) {
- // TODO(171305898): handle multiple displays.
- if (display_number != 0) {
- processed_frame.is_success_ = false;
- return;
- }
- const int display_w =
- ScreenConnectorInfo::ScreenWidth(display_number);
- const int display_h =
- ScreenConnectorInfo::ScreenHeight(display_number);
- const int display_stride_bytes =
- ScreenConnectorInfo::ScreenStrideBytes(display_number);
processed_frame.display_number_ = display_number;
processed_frame.buf_ =
- std::make_unique<CvdVideoFrameBuffer>(display_w, display_h);
+ std::make_unique<CvdVideoFrameBuffer>(frame_width, frame_height);
libyuv::ABGRToI420(
- frame_pixels, display_stride_bytes, processed_frame.buf_->DataY(),
+ frame_pixels, frame_stride_bytes, processed_frame.buf_->DataY(),
processed_frame.buf_->StrideY(), processed_frame.buf_->DataU(),
processed_frame.buf_->StrideU(), processed_frame.buf_->DataV(),
- processed_frame.buf_->StrideV(), display_w, display_h);
+ processed_frame.buf_->StrideV(), frame_width, frame_height);
processed_frame.is_success_ = true;
};
return callback;
@@ -61,11 +52,12 @@
[[noreturn]] void DisplayHandler::Loop() {
for (;;) {
- auto processed_frame = screen_connector_->OnNextFrame();
+ auto processed_frame = screen_connector_.OnNextFrame();
// processed_frame has display number from the guest
{
std::lock_guard<std::mutex> lock(last_buffer_mutex_);
std::shared_ptr<CvdVideoFrameBuffer> buffer = std::move(processed_frame.buf_);
+ last_buffer_display_ = processed_frame.display_number_;
last_buffer_ =
std::static_pointer_cast<webrtc_streaming::VideoFrameBuffer>(buffer);
}
@@ -77,9 +69,11 @@
void DisplayHandler::SendLastFrame() {
std::shared_ptr<webrtc_streaming::VideoFrameBuffer> buffer;
+ std::uint32_t buffer_display;
{
std::lock_guard<std::mutex> lock(last_buffer_mutex_);
buffer = last_buffer_;
+ buffer_display = last_buffer_display_;
}
if (!buffer) {
// If a connection request arrives before the first frame is available don't
@@ -94,21 +88,21 @@
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
- display_sink_->OnFrame(buffer, time_stamp);
+ display_sinks_[buffer_display]->OnFrame(buffer, time_stamp);
}
}
void DisplayHandler::IncClientCount() {
client_count_++;
if (client_count_ == 1) {
- screen_connector_->ReportClientsConnected(true);
+ screen_connector_.ReportClientsConnected(true);
}
}
void DisplayHandler::DecClientCount() {
client_count_--;
if (client_count_ == 0) {
- screen_connector_->ReportClientsConnected(false);
+ screen_connector_.ReportClientsConnected(false);
}
}
diff --git a/host/frontend/webrtc/display_handler.h b/host/frontend/webrtc/display_handler.h
index 3045e07..7703b08 100644
--- a/host/frontend/webrtc/display_handler.h
+++ b/host/frontend/webrtc/display_handler.h
@@ -16,8 +16,9 @@
#pragma once
-#include <mutex>
#include <memory>
+#include <mutex>
+#include <vector>
#include "host/frontend/webrtc/cvd_video_frame_buffer.h"
#include "host/frontend/webrtc/lib/video_sink.h"
@@ -33,9 +34,17 @@
* must be default-constructable & assignable
*
*/
-struct WebRtcScProcessedFrame : ScreenConnectorFrameInfo {
+struct WebRtcScProcessedFrame : public ScreenConnectorFrameInfo {
// must support move semantic
std::unique_ptr<CvdVideoFrameBuffer> buf_;
+ std::unique_ptr<WebRtcScProcessedFrame> Clone() {
+ // copy internal buffer, not move
+ CvdVideoFrameBuffer* new_buffer = new CvdVideoFrameBuffer(*(buf_.get()));
+ auto cloned_frame = std::make_unique<WebRtcScProcessedFrame>();
+ cloned_frame->buf_ =
+ std::move(std::unique_ptr<CvdVideoFrameBuffer>(new_buffer));
+ return std::move(cloned_frame);
+ }
};
class DisplayHandler {
@@ -44,8 +53,8 @@
using GenerateProcessedFrameCallback = ScreenConnector::GenerateProcessedFrameCallback;
DisplayHandler(
- std::shared_ptr<webrtc_streaming::VideoSink> display_sink,
- std::unique_ptr<ScreenConnector> screen_connector);
+ std::vector<std::shared_ptr<webrtc_streaming::VideoSink>> display_sinks,
+ ScreenConnector& screen_connector);
~DisplayHandler() = default;
[[noreturn]] void Loop();
@@ -56,9 +65,10 @@
private:
GenerateProcessedFrameCallback GetScreenConnectorCallback();
- std::shared_ptr<webrtc_streaming::VideoSink> display_sink_;
- std::unique_ptr<ScreenConnector> screen_connector_;
+ std::vector<std::shared_ptr<webrtc_streaming::VideoSink>> display_sinks_;
+ ScreenConnector& screen_connector_;
std::shared_ptr<webrtc_streaming::VideoFrameBuffer> last_buffer_;
+ std::uint32_t last_buffer_display_ = 0;
std::mutex last_buffer_mutex_;
std::mutex next_frame_mutex_;
int client_count_ = 0;
diff --git a/host/frontend/webrtc/kernel_log_events_handler.cpp b/host/frontend/webrtc/kernel_log_events_handler.cpp
index 78cdb73..ca14e6f 100644
--- a/host/frontend/webrtc/kernel_log_events_handler.cpp
+++ b/host/frontend/webrtc/kernel_log_events_handler.cpp
@@ -26,13 +26,10 @@
using namespace android;
namespace cuttlefish {
-namespace webrtc_streaming {
KernelLogEventsHandler::KernelLogEventsHandler(
- SharedFD kernel_log_fd,
- std::function<void(const Json::Value&)> send_to_client)
- : send_to_client_(send_to_client),
- kernel_log_fd_(kernel_log_fd),
+ SharedFD kernel_log_fd)
+ : kernel_log_fd_(kernel_log_fd),
eventfd_(SharedFD::Event()),
running_(true),
read_thread_([this]() { ReadLoop(); }) {}
@@ -76,22 +73,40 @@
if (read_result->event == monitor::Event::BootStarted) {
Json::Value message;
message["event"] = kBootStartedMessage;
- send_to_client_(message);
+ DeliverEvent(message);
}
if (read_result->event == monitor::Event::BootCompleted) {
Json::Value message;
message["event"] = kBootCompletedMessage;
- send_to_client_(message);
+ DeliverEvent(message);
}
if (read_result->event == monitor::Event::ScreenChanged) {
Json::Value message;
message["event"] = kScreenChangedMessage;
message["metadata"] = read_result->metadata;
- send_to_client_(message);
+ DeliverEvent(message);
}
}
}
}
-} // namespace webrtc_streaming
+int KernelLogEventsHandler::AddSubscriber(
+ std::function<void(const Json::Value&)> subscriber) {
+ std::lock_guard<std::mutex> lock(subscribers_mtx_);
+ subscribers_[++last_subscriber_id_] = subscriber;
+ return last_subscriber_id_;
+}
+
+void KernelLogEventsHandler::Unsubscribe(int subscriber_id) {
+ std::lock_guard<std::mutex> lock(subscribers_mtx_);
+ subscribers_.erase(subscriber_id);
+}
+
+void KernelLogEventsHandler::DeliverEvent(const Json::Value& event) {
+ std::lock_guard<std::mutex> lock(subscribers_mtx_);
+ for (const auto& entry : subscribers_) {
+ entry.second(event);
+ }
+}
+
} // namespace cuttlefish
diff --git a/host/frontend/webrtc/kernel_log_events_handler.h b/host/frontend/webrtc/kernel_log_events_handler.h
index 3025f26..5ce99aa 100644
--- a/host/frontend/webrtc/kernel_log_events_handler.h
+++ b/host/frontend/webrtc/kernel_log_events_handler.h
@@ -18,33 +18,35 @@
#include <atomic>
#include <memory>
+#include <mutex>
#include <thread>
+#include <map>
#include <json/json.h>
#include "common/libs/fs/shared_fd.h"
namespace cuttlefish {
-namespace webrtc_streaming {
// Listen to kernel log events and report them to clients.
struct KernelLogEventsHandler {
- explicit KernelLogEventsHandler(SharedFD kernel_log_fd,
- std::function<void(const Json::Value&)> send_to_client);
+ explicit KernelLogEventsHandler(SharedFD kernel_log_fd);
~KernelLogEventsHandler();
+ int AddSubscriber(std::function<void(const Json::Value&)> subscriber);
+ void Unsubscribe(int subscriber_id);
private:
-
- std::function<void(const Json::Value&)> send_to_client_;
-
void ReadLoop();
+ void DeliverEvent(const Json::Value& event);
SharedFD kernel_log_fd_;
SharedFD eventfd_;
std::atomic<bool> running_;
std::thread read_thread_;
+ std::map<int, std::function<void(const Json::Value&)>> subscribers_;
+ int last_subscriber_id_ = 0;
+ std::mutex subscribers_mtx_;
};
-} // namespace webrtc_streaming
} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/camera_controller.h b/host/frontend/webrtc/lib/camera_controller.h
new file mode 100644
index 0000000..403af5b
--- /dev/null
+++ b/host/frontend/webrtc/lib/camera_controller.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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 <json/json.h>
+
+namespace cuttlefish {
+
+class CameraController {
+ public:
+ virtual ~CameraController() = default;
+
+ // Handle data messages coming from the client
+ virtual void HandleMessage(const std::vector<char>& message) = 0;
+ // Handle control messages coming from the client
+ virtual void HandleMessage(const Json::Value& message) = 0;
+ // Send control messages to client
+ virtual void SendMessage(const Json::Value& msg) {
+ if (message_sender_) {
+ message_sender_(msg);
+ }
+ }
+ virtual void SetMessageSender(
+ std::function<bool(const Json::Value& msg)> sender) {
+ message_sender_ = sender;
+ }
+
+ protected:
+ std::function<bool(const Json::Value& msg)> message_sender_;
+};
+
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/camera_streamer.cpp b/host/frontend/webrtc/lib/camera_streamer.cpp
new file mode 100644
index 0000000..7634e28
--- /dev/null
+++ b/host/frontend/webrtc/lib/camera_streamer.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 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 "camera_streamer.h"
+
+#include <android-base/logging.h>
+#include <chrono>
+#include "common/libs/utils/vsock_connection.h"
+
+namespace cuttlefish {
+namespace webrtc_streaming {
+
+CameraStreamer::CameraStreamer(unsigned int port, unsigned int cid)
+ : cid_(cid), port_(port) {}
+
+CameraStreamer::~CameraStreamer() { Disconnect(); }
+
+// We are getting frames from the client so try forwarding those to the CVD
+void CameraStreamer::OnFrame(const webrtc::VideoFrame& client_frame) {
+ std::lock_guard<std::mutex> lock(onframe_mutex_);
+ if (!cvd_connection_.IsConnected() && !pending_connection_.valid()) {
+ // Start new connection
+ pending_connection_ = cvd_connection_.ConnectAsync(port_, cid_);
+ return;
+ } else if (pending_connection_.valid()) {
+ if (!IsConnectionReady()) {
+ return;
+ }
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ if (!cvd_connection_.WriteMessage(settings_buffer_)) {
+ LOG(ERROR) << "Failed writing camera settings:";
+ return;
+ }
+ StartReadLoop();
+ LOG(INFO) << "Connected!";
+ }
+ auto resolution = resolution_.load();
+ if (resolution.height <= 0 || resolution.width <= 0) {
+ // We don't have a valid resolution that is necessary for
+ // potential frame scaling
+ return;
+ }
+ auto frame = client_frame.video_frame_buffer()->ToI420().get();
+ if (frame->width() != resolution.width ||
+ frame->height() != resolution.height) {
+ // incoming resolution does not match with the resolution we
+ // have communicated to the CVD - scaling required
+ if (!scaled_frame_ || resolution.width != scaled_frame_->width() ||
+ resolution.height != scaled_frame_->height()) {
+ scaled_frame_ =
+ webrtc::I420Buffer::Create(resolution.width, resolution.height);
+ }
+ scaled_frame_->CropAndScaleFrom(*frame);
+ frame = scaled_frame_.get();
+ }
+ if (!VsockSendYUVFrame(frame)) {
+ LOG(ERROR) << "Sending frame over vsock failed";
+ }
+}
+
+// Handle message json coming from client
+void CameraStreamer::HandleMessage(const Json::Value& message) {
+ auto command = message["command"].asString();
+ if (command == "camera_settings") {
+ // save local copy of resolution that is required for frame scaling
+ resolution_ = GetResolutionFromSettings(message);
+ Json::StreamWriterBuilder factory;
+ std::string new_settings = Json::writeString(factory, message);
+ if (!settings_buffer_.empty() && new_settings != settings_buffer_) {
+ // Settings have changed - disconnect
+ // Next incoming frames will trigger re-connection
+ Disconnect();
+ }
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ settings_buffer_ = new_settings;
+ LOG(INFO) << "New camera settings received:" << new_settings;
+ }
+}
+
+// Handle binary blobs coming from client
+void CameraStreamer::HandleMessage(const std::vector<char>& message) {
+ LOG(INFO) << "Pass through " << message.size() << "bytes";
+ std::lock_guard<std::mutex> lock(frame_mutex_);
+ cvd_connection_.WriteMessage(message);
+}
+
+CameraStreamer::Resolution CameraStreamer::GetResolutionFromSettings(
+ const Json::Value& settings) {
+ return {.width = settings["width"].asInt(),
+ .height = settings["height"].asInt()};
+}
+
+bool CameraStreamer::VsockSendYUVFrame(
+ const webrtc::I420BufferInterface* frame) {
+ int32_t size = frame->width() * frame->height() +
+ 2 * frame->ChromaWidth() * frame->ChromaHeight();
+ const char* y = reinterpret_cast<const char*>(frame->DataY());
+ const char* u = reinterpret_cast<const char*>(frame->DataU());
+ const char* v = reinterpret_cast<const char*>(frame->DataV());
+ auto chroma_width = frame->ChromaWidth();
+ auto chroma_height = frame->ChromaHeight();
+ std::lock_guard<std::mutex> lock(frame_mutex_);
+ return cvd_connection_.Write(size) &&
+ cvd_connection_.WriteStrides(y, frame->width(), frame->height(),
+ frame->StrideY()) &&
+ cvd_connection_.WriteStrides(u, chroma_width, chroma_height,
+ frame->StrideU()) &&
+ cvd_connection_.WriteStrides(v, chroma_width, chroma_height,
+ frame->StrideV());
+}
+
+bool CameraStreamer::IsConnectionReady() {
+ if (!pending_connection_.valid()) {
+ return cvd_connection_.IsConnected();
+ } else if (pending_connection_.wait_for(std::chrono::seconds(0)) !=
+ std::future_status::ready) {
+ // Still waiting for connection
+ return false;
+ } else if (settings_buffer_.empty()) {
+ // connection is ready but we have not yet received client
+ // camera settings
+ return false;
+ }
+ return pending_connection_.get();
+}
+
+void CameraStreamer::StartReadLoop() {
+ if (reader_thread_.joinable()) {
+ reader_thread_.join();
+ }
+ reader_thread_ = std::thread([this] {
+ while (cvd_connection_.IsConnected()) {
+ auto json_value = cvd_connection_.ReadJsonMessage();
+ if (!json_value.empty()) {
+ SendMessage(json_value);
+ }
+ }
+ LOG(INFO) << "Exit reader thread";
+ });
+}
+
+void CameraStreamer::Disconnect() {
+ cvd_connection_.Disconnect();
+ if (reader_thread_.joinable()) {
+ reader_thread_.join();
+ }
+}
+
+} // namespace webrtc_streaming
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/camera_streamer.h b/host/frontend/webrtc/lib/camera_streamer.h
new file mode 100644
index 0000000..ceab2e6
--- /dev/null
+++ b/host/frontend/webrtc/lib/camera_streamer.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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 <api/video/i420_buffer.h>
+#include <api/video/video_frame.h>
+#include <api/video/video_sink_interface.h>
+#include <json/json.h>
+
+#include "common/libs/utils/vsock_connection.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
+
+#include <atomic>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+namespace cuttlefish {
+namespace webrtc_streaming {
+
+class CameraStreamer : public rtc::VideoSinkInterface<webrtc::VideoFrame>,
+ public CameraController {
+ public:
+ CameraStreamer(unsigned int port, unsigned int cid);
+ ~CameraStreamer();
+
+ CameraStreamer(const CameraStreamer& other) = delete;
+ CameraStreamer& operator=(const CameraStreamer& other) = delete;
+
+ void OnFrame(const webrtc::VideoFrame& frame) override;
+
+ void HandleMessage(const Json::Value& message) override;
+ void HandleMessage(const std::vector<char>& message) override;
+
+ private:
+ using Resolution = struct {
+ int32_t width;
+ int32_t height;
+ };
+ bool ForwardClientMessage(const Json::Value& message);
+ Resolution GetResolutionFromSettings(const Json::Value& settings);
+ bool VsockSendYUVFrame(const webrtc::I420BufferInterface* frame);
+ bool IsConnectionReady();
+ void StartReadLoop();
+ void Disconnect();
+ std::future<bool> pending_connection_;
+ VsockClientConnection cvd_connection_;
+ std::atomic<Resolution> resolution_;
+ std::mutex settings_mutex_;
+ std::string settings_buffer_;
+ std::mutex frame_mutex_;
+ std::mutex onframe_mutex_;
+ rtc::scoped_refptr<webrtc::I420Buffer> scaled_frame_;
+ unsigned int cid_;
+ unsigned int port_;
+ std::thread reader_thread_;
+};
+
+} // namespace webrtc_streaming
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/client_handler.cpp b/host/frontend/webrtc/lib/client_handler.cpp
index 4912465..4893f8d 100644
--- a/host/frontend/webrtc/lib/client_handler.cpp
+++ b/host/frontend/webrtc/lib/client_handler.cpp
@@ -38,6 +38,9 @@
static constexpr auto kInputChannelLabel = "input-channel";
static constexpr auto kAdbChannelLabel = "adb-channel";
+static constexpr auto kBluetoothChannelLabel = "bluetooth-channel";
+static constexpr auto kCameraDataChannelLabel = "camera-data-channel";
+static constexpr auto kCameraDataEof = "EOF";
class CvdCreateSessionDescriptionObserver
: public webrtc::CreateSessionDescriptionObserver {
@@ -149,6 +152,38 @@
std::shared_ptr<ConnectionObserver> observer_;
};
+class BluetoothChannelHandler : public webrtc::DataChannelObserver {
+ public:
+ BluetoothChannelHandler(
+ rtc::scoped_refptr<webrtc::DataChannelInterface> bluetooth_channel,
+ std::shared_ptr<ConnectionObserver> observer);
+ ~BluetoothChannelHandler() override;
+
+ void OnStateChange() override;
+ void OnMessage(const webrtc::DataBuffer &msg) override;
+
+ private:
+ rtc::scoped_refptr<webrtc::DataChannelInterface> bluetooth_channel_;
+ std::shared_ptr<ConnectionObserver> observer_;
+ bool channel_open_reported_ = false;
+};
+
+class CameraChannelHandler : public webrtc::DataChannelObserver {
+ public:
+ CameraChannelHandler(
+ rtc::scoped_refptr<webrtc::DataChannelInterface> bluetooth_channel,
+ std::shared_ptr<ConnectionObserver> observer);
+ ~CameraChannelHandler() override;
+
+ void OnStateChange() override;
+ void OnMessage(const webrtc::DataBuffer &msg) override;
+
+ private:
+ rtc::scoped_refptr<webrtc::DataChannelInterface> camera_channel_;
+ std::shared_ptr<ConnectionObserver> observer_;
+ std::vector<char> receive_buffer_;
+};
+
InputChannelHandler::InputChannelHandler(
rtc::scoped_refptr<webrtc::DataChannelInterface> input_channel,
std::shared_ptr<ConnectionObserver> observer)
@@ -271,7 +306,7 @@
observer_->OnAdbChannelOpen([this](const uint8_t *msg, size_t size) {
webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(msg, size),
true /*binary*/);
- // TODO (jemoreira): When the SCTP channel is congested data channel
+ // TODO (b/185832105): When the SCTP channel is congested data channel
// messages are buffered up to 16MB, when the buffer is full the channel
// is abruptly closed. Keep track of the buffered data to avoid losing the
// adb data channel.
@@ -320,22 +355,91 @@
control_channel_->Send(buffer);
}
+BluetoothChannelHandler::BluetoothChannelHandler(
+ rtc::scoped_refptr<webrtc::DataChannelInterface> bluetooth_channel,
+ std::shared_ptr<ConnectionObserver> observer)
+ : bluetooth_channel_(bluetooth_channel), observer_(observer) {
+ bluetooth_channel_->RegisterObserver(this);
+}
+
+BluetoothChannelHandler::~BluetoothChannelHandler() {
+ bluetooth_channel_->UnregisterObserver();
+}
+
+void BluetoothChannelHandler::OnStateChange() {
+ LOG(VERBOSE) << "Bluetooth channel state changed to "
+ << webrtc::DataChannelInterface::DataStateString(
+ bluetooth_channel_->state());
+}
+
+void BluetoothChannelHandler::OnMessage(const webrtc::DataBuffer &msg) {
+ // Notify bluetooth channel opening when actually using the channel,
+ // it has the same reason with AdbChannelHandler::OnMessage,
+ // to avoid unnecessarily connection for Rootcanal.
+ if (channel_open_reported_ == false) {
+ channel_open_reported_ = true;
+ observer_->OnBluetoothChannelOpen([this](const uint8_t *msg, size_t size) {
+ webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(msg, size),
+ true /*binary*/);
+ // TODO (b/185832105): When the SCTP channel is congested data channel
+ // messages are buffered up to 16MB, when the buffer is full the channel
+ // is abruptly closed. Keep track of the buffered data to avoid losing the
+ // adb data channel.
+ bluetooth_channel_->Send(buffer);
+ return true;
+ });
+ }
+
+ observer_->OnBluetoothMessage(msg.data.cdata(), msg.size());
+}
+
+CameraChannelHandler::CameraChannelHandler(
+ rtc::scoped_refptr<webrtc::DataChannelInterface> camera_channel,
+ std::shared_ptr<ConnectionObserver> observer)
+ : camera_channel_(camera_channel), observer_(observer) {
+ camera_channel_->RegisterObserver(this);
+}
+
+CameraChannelHandler::~CameraChannelHandler() {
+ camera_channel_->UnregisterObserver();
+}
+
+void CameraChannelHandler::OnStateChange() {
+ LOG(VERBOSE) << "Camera channel state changed to "
+ << webrtc::DataChannelInterface::DataStateString(
+ camera_channel_->state());
+}
+
+void CameraChannelHandler::OnMessage(const webrtc::DataBuffer &msg) {
+ auto msg_data = msg.data.cdata<char>();
+ if (msg.size() == strlen(kCameraDataEof) &&
+ !strncmp(msg_data, kCameraDataEof, msg.size())) {
+ // Send complete buffer to observer on EOF marker
+ observer_->OnCameraData(receive_buffer_);
+ receive_buffer_.clear();
+ return;
+ }
+ // Otherwise buffer up data
+ receive_buffer_.insert(receive_buffer_.end(), msg_data,
+ msg_data + msg.size());
+}
+
std::shared_ptr<ClientHandler> ClientHandler::Create(
int client_id, std::shared_ptr<ConnectionObserver> observer,
std::function<void(const Json::Value &)> send_to_client_cb,
- std::function<void()> on_connection_closed_cb) {
+ std::function<void(bool)> on_connection_changed_cb) {
return std::shared_ptr<ClientHandler>(new ClientHandler(
- client_id, observer, send_to_client_cb, on_connection_closed_cb));
+ client_id, observer, send_to_client_cb, on_connection_changed_cb));
}
ClientHandler::ClientHandler(
int client_id, std::shared_ptr<ConnectionObserver> observer,
std::function<void(const Json::Value &)> send_to_client_cb,
- std::function<void()> on_connection_closed_cb)
+ std::function<void(bool)> on_connection_changed_cb)
: client_id_(client_id),
observer_(observer),
send_to_client_(send_to_client_cb),
- on_connection_closed_cb_(on_connection_closed_cb) {}
+ on_connection_changed_cb_(on_connection_changed_cb) {}
ClientHandler::~ClientHandler() {
for (auto &data_channel : data_channels_) {
@@ -347,6 +451,13 @@
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection) {
peer_connection_ = peer_connection;
+ // libwebrtc configures the video encoder with a start bitrate of just 300kbs
+ // which causes it to drop the first 4 frames it receives. Any value over 2Mbs
+ // will be capped at 2Mbs when passed to the encoder by the peer_connection
+ // object, so we pass the maximum possible value here.
+ webrtc::BitrateSettings bitrate_settings;
+ bitrate_settings.start_bitrate_bps = 2000000; // 2Mbs
+ peer_connection_->SetBitrate(bitrate_settings);
// At least one data channel needs to be created on the side that makes the
// SDP offer (the device) for data channels to be enabled at all.
// This channel is meant to carry control commands from the client.
@@ -388,6 +499,17 @@
return true;
}
+webrtc::VideoTrackInterface *ClientHandler::GetCameraStream() const {
+ for (const auto &tranceiver : peer_connection_->GetTransceivers()) {
+ auto track = tranceiver->receiver()->track();
+ if (track &&
+ track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
+ return static_cast<webrtc::VideoTrackInterface *>(track.get());
+ }
+ }
+ return nullptr;
+}
+
void ClientHandler::LogAndReplyError(const std::string &error_msg) const {
LOG(ERROR) << error_msg;
Json::Value reply;
@@ -545,7 +667,7 @@
// will then wait for the callback to return -> deadlock). Destroying the
// peer_connection_ has the same effect. The only alternative is to postpone
// that operation until after the callback returns.
- on_connection_closed_cb_();
+ on_connection_changed_cb_(false);
}
void ClientHandler::OnConnectionChange(
@@ -563,6 +685,7 @@
control_handler_->Send(msg, size, binary);
return true;
});
+ on_connection_changed_cb_(true);
break;
case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected:
LOG(VERBOSE) << "Client " << client_id_ << ": Connection disconnected";
@@ -602,6 +725,12 @@
input_handler_.reset(new InputChannelHandler(data_channel, observer_));
} else if (label == kAdbChannelLabel) {
adb_handler_.reset(new AdbChannelHandler(data_channel, observer_));
+ } else if (label == kBluetoothChannelLabel) {
+ bluetooth_handler_.reset(
+ new BluetoothChannelHandler(data_channel, observer_));
+ } else if (label == kCameraDataChannelLabel) {
+ camera_data_handler_.reset(
+ new CameraChannelHandler(data_channel, observer_));
} else {
LOG(VERBOSE) << "Data channel connected: " << label;
data_channels_.push_back(data_channel);
diff --git a/host/frontend/webrtc/lib/client_handler.h b/host/frontend/webrtc/lib/client_handler.h
index b13a502..f7f587b 100644
--- a/host/frontend/webrtc/lib/client_handler.h
+++ b/host/frontend/webrtc/lib/client_handler.h
@@ -21,6 +21,7 @@
#include <optional>
#include <sstream>
#include <string>
+#include <utility>
#include <vector>
#include <json/json.h>
@@ -36,6 +37,8 @@
class InputChannelHandler;
class AdbChannelHandler;
class ControlChannelHandler;
+class BluetoothChannelHandler;
+class CameraChannelHandler;
class ClientHandler : public webrtc::PeerConnectionObserver,
public std::enable_shared_from_this<ClientHandler> {
@@ -43,7 +46,7 @@
static std::shared_ptr<ClientHandler> Create(
int client_id, std::shared_ptr<ConnectionObserver> observer,
std::function<void(const Json::Value&)> send_client_cb,
- std::function<void()> on_connection_closed_cb);
+ std::function<void(bool)> on_connection_changed_cb);
~ClientHandler() override;
bool SetPeerConnection(
@@ -55,6 +58,8 @@
bool AddAudio(rtc::scoped_refptr<webrtc::AudioTrackInterface> track,
const std::string& label);
+ webrtc::VideoTrackInterface* GetCameraStream() const;
+
void HandleMessage(const Json::Value& client_message);
// CreateSessionDescriptionObserver implementation
@@ -106,7 +111,7 @@
};
ClientHandler(int client_id, std::shared_ptr<ConnectionObserver> observer,
std::function<void(const Json::Value&)> send_client_cb,
- std::function<void()> on_connection_closed_cb);
+ std::function<void(bool)> on_connection_changed_cb);
// Intentionally private, disconnect the client by destroying the object.
void Close();
@@ -117,12 +122,14 @@
State state_ = State::kNew;
std::shared_ptr<ConnectionObserver> observer_;
std::function<void(const Json::Value&)> send_to_client_;
- std::function<void()> on_connection_closed_cb_;
+ std::function<void(bool)> on_connection_changed_cb_;
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
std::vector<rtc::scoped_refptr<webrtc::DataChannelInterface>> data_channels_;
std::unique_ptr<InputChannelHandler> input_handler_;
std::unique_ptr<AdbChannelHandler> adb_handler_;
std::unique_ptr<ControlChannelHandler> control_handler_;
+ std::unique_ptr<BluetoothChannelHandler> bluetooth_handler_;
+ std::unique_ptr<CameraChannelHandler> camera_data_handler_;
};
} // namespace webrtc_streaming
diff --git a/host/frontend/webrtc/lib/connection_observer.h b/host/frontend/webrtc/lib/connection_observer.h
index 149ffdc..fe82549 100644
--- a/host/frontend/webrtc/lib/connection_observer.h
+++ b/host/frontend/webrtc/lib/connection_observer.h
@@ -42,6 +42,10 @@
virtual void OnControlChannelOpen(
std::function<bool(const Json::Value)> control_message_sender) = 0;
virtual void OnControlMessage(const uint8_t* msg, size_t size) = 0;
+ virtual void OnBluetoothChannelOpen(
+ std::function<bool(const uint8_t*, size_t)> bluetooth_message_sender) = 0;
+ virtual void OnBluetoothMessage(const uint8_t* msg, size_t size) = 0;
+ virtual void OnCameraData(const std::vector<char>& data) = 0;
};
class ConnectionObserverFactory {
diff --git a/host/frontend/webrtc/lib/streamer.cpp b/host/frontend/webrtc/lib/streamer.cpp
index aa06108..71bbab5 100644
--- a/host/frontend/webrtc/lib/streamer.cpp
+++ b/host/frontend/webrtc/lib/streamer.cpp
@@ -34,6 +34,7 @@
#include "host/frontend/webrtc/lib/audio_device.h"
#include "host/frontend/webrtc/lib/audio_track_source_impl.h"
+#include "host/frontend/webrtc/lib/camera_streamer.h"
#include "host/frontend/webrtc/lib/client_handler.h"
#include "host/frontend/webrtc/lib/port_range_socket_factory.h"
#include "host/frontend/webrtc/lib/video_track_source_impl.h"
@@ -56,6 +57,9 @@
constexpr auto kControlPanelButtonTitle = "title";
constexpr auto kControlPanelButtonIconName = "icon_name";
constexpr auto kControlPanelButtonShellCommand = "shell_command";
+constexpr auto kControlPanelButtonDeviceStates = "device_states";
+constexpr auto kControlPanelButtonLidSwitchOpen = "lid_switch_open";
+constexpr auto kControlPanelButtonHingeAngleValue = "hinge_angle_value";
constexpr auto kCustomControlPanelButtonsField = "custom_control_panel_buttons";
void SendJson(WsConnection* ws_conn, const Json::Value& data) {
@@ -100,6 +104,7 @@
std::string title;
std::string icon_name;
std::optional<std::string> shell_command;
+ std::vector<DeviceState> device_states;
};
// TODO (jemoreira): move to a place in common with the signaling server
@@ -137,6 +142,7 @@
void SendMessageToClient(int client_id, const Json::Value& msg);
void DestroyClientHandler(int client_id);
+ void SetupCameraForClient(int client_id);
// WsObserver
void OnOpen() override;
@@ -166,6 +172,7 @@
std::map<std::string, std::string> hardware_;
std::vector<ControlPanelButtonDescriptor> custom_control_panel_buttons_;
std::shared_ptr<AudioDeviceModuleWrapper> audio_device_module_;
+ std::unique_ptr<CameraStreamer> camera_streamer_;
};
Streamer::Streamer(std::unique_ptr<Streamer::Impl> impl)
@@ -259,16 +266,39 @@
return impl_->audio_device_module_;
}
+CameraController* Streamer::AddCamera(unsigned int port, unsigned int cid) {
+ impl_->camera_streamer_ = std::make_unique<CameraStreamer>(port, cid);
+ return impl_->camera_streamer_.get();
+}
+
void Streamer::SetHardwareSpec(std::string key, std::string value) {
impl_->hardware_.emplace(key, value);
}
-void Streamer::AddCustomControlPanelButton(
+void Streamer::AddCustomControlPanelButton(const std::string& command,
+ const std::string& title,
+ const std::string& icon_name) {
+ ControlPanelButtonDescriptor button = {
+ .command = command, .title = title, .icon_name = icon_name};
+ impl_->custom_control_panel_buttons_.push_back(button);
+}
+
+void Streamer::AddCustomControlPanelButtonWithShellCommand(
+ const std::string& command, const std::string& title,
+ const std::string& icon_name, const std::string& shell_command) {
+ ControlPanelButtonDescriptor button = {
+ .command = command, .title = title, .icon_name = icon_name};
+ button.shell_command = shell_command;
+ impl_->custom_control_panel_buttons_.push_back(button);
+}
+
+void Streamer::AddCustomControlPanelButtonWithDeviceStates(
const std::string& command, const std::string& title,
const std::string& icon_name,
- const std::optional<std::string>& shell_command) {
- ControlPanelButtonDescriptor button = {command, title, icon_name,
- shell_command};
+ const std::vector<DeviceState>& device_states) {
+ ControlPanelButtonDescriptor button = {
+ .command = command, .title = title, .icon_name = icon_name};
+ button.device_states = device_states;
impl_->custom_control_panel_buttons_.push_back(button);
}
@@ -359,6 +389,21 @@
button_entry[kControlPanelButtonIconName] = button.icon_name;
if (button.shell_command) {
button_entry[kControlPanelButtonShellCommand] = *(button.shell_command);
+ } else if (!button.device_states.empty()) {
+ Json::Value device_states(Json::arrayValue);
+ for (const DeviceState& device_state : button.device_states) {
+ Json::Value device_state_entry;
+ if (device_state.lid_switch_open) {
+ device_state_entry[kControlPanelButtonLidSwitchOpen] =
+ *device_state.lid_switch_open;
+ }
+ if (device_state.hinge_angle_value) {
+ device_state_entry[kControlPanelButtonHingeAngleValue] =
+ *device_state.hinge_angle_value;
+ }
+ device_states.append(device_state_entry);
+ }
+ button_entry[kControlPanelButtonDeviceStates] = device_states;
}
custom_control_panel_buttons.append(button_entry);
}
@@ -529,7 +574,13 @@
[this, client_id](const Json::Value& msg) {
SendMessageToClient(client_id, msg);
},
- [this, client_id] { DestroyClientHandler(client_id); });
+ [this, client_id](bool isOpen) {
+ if (isOpen) {
+ SetupCameraForClient(client_id);
+ } else {
+ DestroyClientHandler(client_id);
+ }
+ });
webrtc::PeerConnectionInterface::RTCConfiguration config;
config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
@@ -604,5 +655,19 @@
});
}
+void Streamer::Impl::SetupCameraForClient(int client_id) {
+ if (!camera_streamer_) {
+ return;
+ }
+ auto client_handler = clients_[client_id];
+ if (client_handler) {
+ auto camera_track = client_handler->GetCameraStream();
+ if (camera_track) {
+ camera_track->AddOrUpdateSink(camera_streamer_.get(),
+ rtc::VideoSinkWants());
+ }
+ }
+}
+
} // namespace webrtc_streaming
} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/streamer.h b/host/frontend/webrtc/lib/streamer.h
index 90f20e4..50a7e12 100644
--- a/host/frontend/webrtc/lib/streamer.h
+++ b/host/frontend/webrtc/lib/streamer.h
@@ -24,8 +24,11 @@
#include <utility>
#include <vector>
+#include "host/libs/config/custom_actions.h"
+
#include "host/frontend/webrtc/lib/audio_sink.h"
#include "host/frontend/webrtc/lib/audio_source.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
#include "host/frontend/webrtc/lib/connection_observer.h"
#include "host/frontend/webrtc/lib/local_recorder.h"
#include "host/frontend/webrtc/lib/video_sink.h"
@@ -96,13 +99,19 @@
// stream here.
std::shared_ptr<AudioSource> GetAudioSource();
+ CameraController* AddCamera(unsigned int port, unsigned int cid);
+
// Add a custom button to the control panel.
- // If this button should be handled by an action server, use nullopt (the
- // default) for shell_command.
- void AddCustomControlPanelButton(
+ void AddCustomControlPanelButton(const std::string& command,
+ const std::string& title,
+ const std::string& icon_name);
+ void AddCustomControlPanelButtonWithShellCommand(
+ const std::string& command, const std::string& title,
+ const std::string& icon_name, const std::string& shell_command);
+ void AddCustomControlPanelButtonWithDeviceStates(
const std::string& command, const std::string& title,
const std::string& icon_name,
- const std::optional<std::string>& shell_command = std::nullopt);
+ const std::vector<DeviceState>& device_states);
// Register with the operator.
void Register(std::weak_ptr<OperatorObserver> operator_observer);
diff --git a/host/frontend/webrtc/lib/utils.cpp b/host/frontend/webrtc/lib/utils.cpp
index 117492d..78460c3 100644
--- a/host/frontend/webrtc/lib/utils.cpp
+++ b/host/frontend/webrtc/lib/utils.cpp
@@ -23,23 +23,47 @@
namespace cuttlefish {
namespace webrtc_streaming {
+namespace {
+
+std::string ValidateField(const Json::Value &obj, const std::string &type,
+ const std::string &field_name,
+ const Json::ValueType &field_type, bool required) {
+ if (!obj.isMember(field_name) && !required) {
+ return "";
+ }
+ if (!(obj.isMember(field_name) &&
+ obj[field_name].isConvertibleTo(field_type))) {
+ std::string error_msg = "Expected a field named '";
+ error_msg += field_name + "' of type '";
+ error_msg += std::to_string(field_type);
+ error_msg += "'";
+ if (!type.empty()) {
+ error_msg += " in message of type '" + type + "'";
+ }
+ error_msg += ".";
+ return error_msg;
+ }
+ return "";
+}
+
+} // namespace
+
ValidationResult ValidationResult::ValidateJsonObject(
const Json::Value &obj, const std::string &type,
- const std::map<std::string, Json::ValueType> &fields) {
- for (const auto &field_spec : fields) {
- const auto &field_name = field_spec.first;
- auto field_type = field_spec.second;
- if (!(obj.isMember(field_name) &&
- obj[field_name].isConvertibleTo(field_type))) {
- std::string error_msg = "Expected a field named '";
- error_msg += field_name + "' of type '";
- error_msg += std::to_string(field_type);
- error_msg += "'";
- if (!type.empty()) {
- error_msg += " in message of type '" + type + "'";
- }
- error_msg += ".";
- return {error_msg};
+ const std::map<std::string, Json::ValueType> &required_fields,
+ const std::map<std::string, Json::ValueType> &optional_fields) {
+ for (const auto &field_spec : required_fields) {
+ auto result =
+ ValidateField(obj, type, field_spec.first, field_spec.second, true);
+ if (!result.empty()) {
+ return {result};
+ }
+ }
+ for (const auto &field_spec : optional_fields) {
+ auto result =
+ ValidateField(obj, type, field_spec.first, field_spec.second, false);
+ if (!result.empty()) {
+ return {result};
}
}
return {};
diff --git a/host/frontend/webrtc/lib/utils.h b/host/frontend/webrtc/lib/utils.h
index 1208551..169221c 100644
--- a/host/frontend/webrtc/lib/utils.h
+++ b/host/frontend/webrtc/lib/utils.h
@@ -32,8 +32,9 @@
// Helper method to ensure a json object has the required fields convertible
// to the appropriate types.
static ValidationResult ValidateJsonObject(
- const Json::Value &obj, const std::string &type,
- const std::map<std::string, Json::ValueType> &fields);
+ const Json::Value &obj, const std::string &type,
+ const std::map<std::string, Json::ValueType> &required_fields,
+ const std::map<std::string, Json::ValueType> &optional_fields = {});
bool ok() const { return !error_.has_value(); }
std::string error() const { return error_.value_or(""); }
diff --git a/host/frontend/webrtc/main.cpp b/host/frontend/webrtc/main.cpp
index f9b29fd..052cce5 100644
--- a/host/frontend/webrtc/main.cpp
+++ b/host/frontend/webrtc/main.cpp
@@ -32,14 +32,20 @@
#include "host/frontend/webrtc/audio_handler.h"
#include "host/frontend/webrtc/connection_observer.h"
#include "host/frontend/webrtc/display_handler.h"
+#include "host/frontend/webrtc/kernel_log_events_handler.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
#include "host/frontend/webrtc/lib/local_recorder.h"
#include "host/frontend/webrtc/lib/streamer.h"
+#include "host/frontend/webrtc/lib/video_sink.h"
#include "host/libs/audio_connector/server.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/config/logging.h"
+#include "host/libs/confui/host_mode_ctrl.h"
+#include "host/libs/confui/host_server.h"
#include "host/libs/screen_connector/screen_connector.h"
-DEFINE_int32(touch_fd, -1, "An fd to listen on for touch connections.");
+DEFINE_string(touch_fds, "",
+ "A list of fds to listen on for touch connections.");
DEFINE_int32(keyboard_fd, -1, "An fd to listen on for keyboard connections.");
DEFINE_int32(switches_fd, -1, "An fd to listen on for switch connections.");
DEFINE_int32(frame_server_fd, -1, "An fd to listen on for frame updates");
@@ -52,13 +58,16 @@
DEFINE_bool(write_virtio_input, true,
"Whether to send input events in virtio format.");
DEFINE_int32(audio_server_fd, -1, "An fd to listen on for audio frames");
+DEFINE_int32(camera_streamer_fd, -1, "An fd to send client camera frames");
using cuttlefish::AudioHandler;
using cuttlefish::CfConnectionObserverFactory;
using cuttlefish::DisplayHandler;
+using cuttlefish::KernelLogEventsHandler;
using cuttlefish::webrtc_streaming::LocalRecorder;
using cuttlefish::webrtc_streaming::Streamer;
using cuttlefish::webrtc_streaming::StreamerConfig;
+using cuttlefish::webrtc_streaming::VideoSink;
class CfOperatorObserver
: public cuttlefish::webrtc_streaming::OperatorObserver {
@@ -68,10 +77,10 @@
LOG(VERBOSE) << "Registered with Operator";
}
virtual void OnClose() override {
- LOG(FATAL) << "Connection with Operator unexpectedly closed";
+ LOG(ERROR) << "Connection with Operator unexpectedly closed";
}
virtual void OnError() override {
- LOG(FATAL) << "Error encountered in connection with Operator";
+ LOG(ERROR) << "Error encountered in connection with Operator";
}
};
@@ -128,11 +137,16 @@
cuttlefish::InputSockets input_sockets;
- input_sockets.touch_server = cuttlefish::SharedFD::Dup(FLAGS_touch_fd);
+ auto counter = 0;
+ for (const auto& touch_fd_str : android::base::Split(FLAGS_touch_fds, ",")) {
+ auto touch_fd = std::stoi(touch_fd_str);
+ input_sockets.touch_servers["display_" + std::to_string(counter++)] =
+ cuttlefish::SharedFD::Dup(touch_fd);
+ close(touch_fd);
+ }
input_sockets.keyboard_server = cuttlefish::SharedFD::Dup(FLAGS_keyboard_fd);
input_sockets.switches_server = cuttlefish::SharedFD::Dup(FLAGS_switches_fd);
auto control_socket = cuttlefish::SharedFD::Dup(FLAGS_command_fd);
- close(FLAGS_touch_fd);
close(FLAGS_keyboard_fd);
close(FLAGS_switches_fd);
close(FLAGS_command_fd);
@@ -141,19 +155,25 @@
// devices have been initialized. That's OK though, because without those
// devices there is no meaningful interaction the user can have with the
// device.
- input_sockets.touch_client =
- cuttlefish::SharedFD::Accept(*input_sockets.touch_server);
+ for (const auto& touch_entry : input_sockets.touch_servers) {
+ input_sockets.touch_clients[touch_entry.first] =
+ cuttlefish::SharedFD::Accept(*touch_entry.second);
+ }
input_sockets.keyboard_client =
cuttlefish::SharedFD::Accept(*input_sockets.keyboard_server);
input_sockets.switches_client =
cuttlefish::SharedFD::Accept(*input_sockets.switches_server);
- std::thread touch_accepter([&input_sockets]() {
- for (;;) {
- input_sockets.touch_client =
- cuttlefish::SharedFD::Accept(*input_sockets.touch_server);
- }
- });
+ std::vector<std::thread> touch_accepters;
+ for (const auto& touch : input_sockets.touch_servers) {
+ auto label = touch.first;
+ touch_accepters.emplace_back([label, &input_sockets]() {
+ for (;;) {
+ input_sockets.touch_clients[label] =
+ cuttlefish::SharedFD::Accept(*input_sockets.touch_servers[label]);
+ }
+ });
+ }
std::thread keyboard_accepter([&input_sockets]() {
for (;;) {
input_sockets.keyboard_client =
@@ -173,8 +193,16 @@
auto cvd_config = cuttlefish::CuttlefishConfig::Get();
auto instance = cvd_config->ForDefaultInstance();
- auto screen_connector =
- cuttlefish::DisplayHandler::ScreenConnector::Get(FLAGS_frame_server_fd);
+ auto& host_mode_ctrl = cuttlefish::HostModeCtrl::Get();
+ auto screen_connector_ptr = cuttlefish::DisplayHandler::ScreenConnector::Get(
+ FLAGS_frame_server_fd, host_mode_ctrl);
+ auto& screen_connector = *(screen_connector_ptr.get());
+
+ // create confirmation UI service, giving host_mode_ctrl and
+ // screen_connector
+ // keep this singleton object alive until the webRTC process ends
+ static auto& host_confui_server =
+ cuttlefish::confui::HostServer::Get(host_mode_ctrl, screen_connector);
StreamerConfig streamer_config;
@@ -184,27 +212,49 @@
streamer_config.operator_server.addr = cvd_config->sig_server_address();
streamer_config.operator_server.port = cvd_config->sig_server_port();
streamer_config.operator_server.path = cvd_config->sig_server_path();
- streamer_config.operator_server.security =
- cvd_config->sig_server_strict()
- ? WsConnection::Security::kStrict
- : WsConnection::Security::kAllowSelfSigned;
+ if (cvd_config->sig_server_secure()) {
+ streamer_config.operator_server.security =
+ cvd_config->sig_server_strict()
+ ? WsConnection::Security::kStrict
+ : WsConnection::Security::kAllowSelfSigned;
+ } else {
+ streamer_config.operator_server.security =
+ WsConnection::Security::kInsecure;
+ }
if (!cvd_config->sig_server_headers_path().empty()) {
streamer_config.operator_server.http_headers =
ParseHttpHeaders(cvd_config->sig_server_headers_path());
}
+ KernelLogEventsHandler kernel_logs_event_handler(kernel_log_events_client);
auto observer_factory = std::make_shared<CfConnectionObserverFactory>(
- input_sockets, kernel_log_events_client);
+ input_sockets, &kernel_logs_event_handler, host_confui_server);
auto streamer = Streamer::Create(streamer_config, observer_factory);
CHECK(streamer) << "Could not create streamer";
- auto display_0 = streamer->AddDisplay(
- "display_0", screen_connector->ScreenWidth(0),
- screen_connector->ScreenHeight(0), cvd_config->dpi(), true);
+ uint32_t display_index = 0;
+ std::vector<std::shared_ptr<VideoSink>> displays;
+ for (const auto& display_config : cvd_config->display_configs()) {
+ const std::string display_id = "display_" + std::to_string(display_index);
+
+ auto display =
+ streamer->AddDisplay(display_id, display_config.width,
+ display_config.height, display_config.dpi, true);
+ displays.push_back(display);
+
+ ++display_index;
+ }
+
auto display_handler =
- std::make_shared<DisplayHandler>(display_0, std::move(screen_connector));
+ std::make_shared<DisplayHandler>(std::move(displays), screen_connector);
+
+ if (instance.camera_server_port()) {
+ auto camera_controller = streamer->AddCamera(instance.camera_server_port(),
+ instance.vsock_guest_cid());
+ observer_factory->SetCameraHandler(camera_controller);
+ }
std::unique_ptr<cuttlefish::webrtc_streaming::LocalRecorder> local_recorder;
if (cvd_config->record_screen()) {
@@ -272,11 +322,10 @@
<< *(custom_action.shell_command);
}
const auto button = custom_action.buttons[0];
- streamer->AddCustomControlPanelButton(button.command, button.title,
- button.icon_name,
- custom_action.shell_command);
- }
- if (custom_action.server) {
+ streamer->AddCustomControlPanelButtonWithShellCommand(
+ button.command, button.title, button.icon_name,
+ *(custom_action.shell_command));
+ } else if (custom_action.server) {
if (action_server_fds.find(*(custom_action.server)) !=
action_server_fds.end()) {
LOG(INFO) << "Connecting to custom action server "
@@ -303,6 +352,15 @@
LOG(ERROR) << "Custom action server not provided as command line flag: "
<< *(custom_action.server);
}
+ } else if (!custom_action.device_states.empty()) {
+ if (custom_action.buttons.size() != 1) {
+ LOG(FATAL)
+ << "Expected exactly one button for custom action device states.";
+ }
+ const auto button = custom_action.buttons[0];
+ streamer->AddCustomControlPanelButtonWithDeviceStates(
+ button.command, button.title, button.icon_name,
+ custom_action.device_states);
}
}
@@ -332,6 +390,7 @@
if (audio_handler) {
audio_handler->Start();
}
+ host_confui_server.Start();
display_handler->Loop();
return 0;
diff --git a/host/frontend/webrtc_operator/Android.bp b/host/frontend/webrtc_operator/Android.bp
index 5c0f888..54eda44 100644
--- a/host/frontend/webrtc_operator/Android.bp
+++ b/host/frontend/webrtc_operator/Android.bp
@@ -109,6 +109,13 @@
}
prebuilt_usr_share_host {
+ name: "webrtc_rootcanal.js",
+ src: "assets/js/rootcanal.js",
+ filename: "rootcanal.js",
+ sub_dir: "webrtc/assets/js",
+}
+
+prebuilt_usr_share_host {
name: "webrtc_server.crt",
src: "certs/server.crt",
filename: "server.crt",
diff --git a/host/frontend/webrtc_operator/assets/controls.css b/host/frontend/webrtc_operator/assets/controls.css
index 5bb0cce..a2dce53 100644
--- a/host/frontend/webrtc_operator/assets/controls.css
+++ b/host/frontend/webrtc_operator/assets/controls.css
@@ -19,15 +19,15 @@
padding-right: 7px;
border-radius: 10px;
background-color: #5f6368; /* Google grey 700 */
- width: 117px;
- height: 64px;
+ width: 80px;
+ height: 44px;
}
.toggle-control .toggle-control-icon {
position: relative;
display: inline-block;
float: left;
- font-size: 64px;
+ font-size: 44px;
color: #e8eaed;
}
@@ -35,9 +35,9 @@
position: relative;
display: inline-block;
float:left;
- width: 52px;
- height: 30px;
- top: 17px;
+ width: 36px;
+ height: 21px;
+ top: 11px;
}
.toggle-control .toggle-control-switch input {
@@ -55,16 +55,16 @@
bottom: 0;
-webkit-transition: .4s;
transition: .4s;
- border-radius: 30px;
- border: solid 5px;
+ border-radius: 21px;
+ border: solid 4px;
border-color: #e8eaed;
}
.toggle-control .toggle-control-slider:before {
position: absolute;
content: "";
- height: 18px;
- width: 18px;
+ height: 12px;
+ width: 12px;
left: 1px;
bottom: 1px;
background-color: #e8eaed;
@@ -82,7 +82,7 @@
}
.toggle-control input:checked + .toggle-control-slider:before {
- -webkit-transform: translateX(22px);
- -ms-transform: translateX(22px);
- transform: translateX(22px);
+ -webkit-transform: translateX(15px);
+ -ms-transform: translateX(15px);
+ transform: translateX(15px);
}
diff --git a/host/frontend/webrtc_operator/assets/index.html b/host/frontend/webrtc_operator/assets/index.html
index 7ab6a72..080718e 100644
--- a/host/frontend/webrtc_operator/assets/index.html
+++ b/host/frontend/webrtc_operator/assets/index.html
@@ -22,9 +22,11 @@
<link rel="stylesheet" type="text/css" href="controls.css" >
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
+ <div id="error-message-div"><h3 id="error-message" class="error"></h3></div>
<section id='device-selector'>
<h1>Available devices <span id='refresh-list'>↻</span></h1>
<ul id="device-list"></ul>
@@ -40,29 +42,43 @@
<h3 id='status-message' class='connecting'>Connecting to device</h3>
</div>
</div>
- <div id='controls-and-screens'>
+ <div id='controls-and-displays'>
<div id='control-panel-default-buttons' class='control-panel-column'>
<button id='device-details-button' title='Device Details' class='material-icons'>
settings
</button>
+ <button id='bluetooth-console-button' title='Bluetooth console' class='material-icons'>
+ settings_bluetooth
+ </button>
</div>
<div id='control-panel-custom-buttons' class='control-panel-column'></div>
- <div id='screens'>
- <video id="device-screen" autoplay ></video>
+ <div id='device-displays'>
</div>
</div>
</section>
- <div id='device-details-modal'>
- <div id='device-details-modal-header'>
+ <div id='device-details-modal' class='modal'>
+ <div id='device-details-modal-header' class='modal-header'>
<h2>Device Details</h2>
- <button id='device-details-close' title='Close' class='material-icons'>close</button>
+ <button id='device-details-close' title='Close' class='material-icons modal-close'>close</button>
</div>
<hr>
<h3>Hardware Configuration</h3>
<span id='device-details-hardware'>unknown</span>
</div>
-
+ <div id='bluetooth-console-modal' class='modal'>
+ <div id='bluetooth-console-modal-header' class='modal-header'>
+ <h2>Bluetooth Console</h2>
+ <button id='bluetooth-console-close' title='Close' class='material-icons modal-close'>close</button>
+ </div>
+ <div>
+ <table>
+ <tr><td colspan='2'><textarea id='bluetooth-console-view' readonly rows='10' cols='60'></textarea></td></tr>
+ <tr><td width='1'><p id='bluetooth-console-cmd-label'>Command:</p></td><td width='100'><input id='bluetooth-console-input' type='text'></input></td></tr>
+ </table>
+ </div>
+ </div>
<script src="js/adb.js"></script>
+ <script src="js/rootcanal.js"></script>
<script src="js/cf_webrtc.js" type="module"></script>
<script src="js/controls.js"></script>
<script src="js/app.js"></script>
diff --git a/host/frontend/webrtc_operator/assets/js/adb.js b/host/frontend/webrtc_operator/assets/js/adb.js
index 75540ae..b011114 100644
--- a/host/frontend/webrtc_operator/assets/js/adb.js
+++ b/host/frontend/webrtc_operator/assets/js/adb.js
@@ -29,69 +29,67 @@
let array = new Uint8Array();
function setU32LE(array, offset, x) {
- array[offset] = x & 0xff;
- array[offset + 1] = (x >> 8) & 0xff;
- array[offset + 2] = (x >> 16) & 0xff;
- array[offset + 3] = x >> 24;
+ array[offset] = x & 0xff;
+ array[offset + 1] = (x >> 8) & 0xff;
+ array[offset + 2] = (x >> 16) & 0xff;
+ array[offset + 3] = x >> 24;
}
function getU32LE(array, offset) {
- let x = array[offset]
- | (array[offset + 1] << 8)
- | (array[offset + 2] << 16)
- | (array[offset + 3] << 24);
+ let x = array[offset] | (array[offset + 1] << 8) | (array[offset + 2] << 16) |
+ (array[offset + 3] << 24);
- return x >>> 0; // convert signed to unsigned if necessary.
+ return x >>> 0; // convert signed to unsigned if necessary.
}
function computeChecksum(array) {
- let sum = 0;
- let i;
- for (i = 0; i < array.length; ++i) {
- sum = ((sum + array[i]) & 0xffffffff) >>> 0;
- }
+ let sum = 0;
+ let i;
+ for (i = 0; i < array.length; ++i) {
+ sum = ((sum + array[i]) & 0xffffffff) >>> 0;
+ }
- return sum;
+ return sum;
}
function createAdbMessage(command, arg0, arg1, payload) {
- let arrayBuffer = new ArrayBuffer(24 + payload.length);
- let array = new Uint8Array(arrayBuffer);
- setU32LE(array, 0, command);
- setU32LE(array, 4, arg0);
- setU32LE(array, 8, arg1);
- setU32LE(array, 12, payload.length);
- setU32LE(array, 16, computeChecksum(payload));
- setU32LE(array, 20, command ^ 0xffffffff);
- array.set(payload, 24);
+ let arrayBuffer = new ArrayBuffer(24 + payload.length);
+ let array = new Uint8Array(arrayBuffer);
+ setU32LE(array, 0, command);
+ setU32LE(array, 4, arg0);
+ setU32LE(array, 8, arg1);
+ setU32LE(array, 12, payload.length);
+ setU32LE(array, 16, computeChecksum(payload));
+ setU32LE(array, 20, command ^ 0xffffffff);
+ array.set(payload, 24);
- return arrayBuffer;
+ return arrayBuffer;
}
function adbOpenConnection() {
- let systemIdentity = utf8Encoder.encode("Cray_II:1234:whatever");
+ let systemIdentity = utf8Encoder.encode('Cray_II:1234:whatever');
- let arrayBuffer = createAdbMessage(
- A_CNXN, 0x1000000, 256 * 1024, systemIdentity);
+ let arrayBuffer =
+ createAdbMessage(A_CNXN, 0x1000000, 256 * 1024, systemIdentity);
- adb_ws.send(arrayBuffer);
+ adb_ws.send(arrayBuffer);
}
function adbShell(command) {
- let destination = utf8Encoder.encode("shell:" + command);
+ let destination = utf8Encoder.encode('shell:' + command);
- let arrayBuffer = createAdbMessage(A_OPEN, kLocalChannelId, 0, destination);
- adb_ws.send(arrayBuffer);
- awaitConnection();
+ let arrayBuffer = createAdbMessage(A_OPEN, kLocalChannelId, 0, destination);
+ adb_ws.send(arrayBuffer);
+ awaitConnection();
}
function adbSendOkay(remoteId) {
- let payload = new Uint8Array(0);
+ let payload = new Uint8Array(0);
- let arrayBuffer = createAdbMessage(
- A_OKAY, kLocalChannelId, remoteId, payload);
+ let arrayBuffer =
+ createAdbMessage(A_OKAY, kLocalChannelId, remoteId, payload);
- adb_ws.send(arrayBuffer);
+ adb_ws.send(arrayBuffer);
}
function JoinArrays(arr1, arr2) {
@@ -101,10 +99,12 @@
return arr;
}
-// Simple lifecycle management that executes callbacks based on connection state.
+// Simple lifecycle management that executes callbacks based on connection
+// state.
//
-// Any attempt to initiate a command (e.g. creating a connection, sending a message)
-// (re)starts a timer. Any response back from any command stops that timer.
+// Any attempt to initiate a command (e.g. creating a connection, sending a
+// message) (re)starts a timer. Any response back from any command stops that
+// timer.
const timeoutMs = 3000;
let connectedCb;
let disconnectedCb;
@@ -125,80 +125,77 @@
}
function adbOnMessage(arrayBuffer) {
- // console.log("adb_ws: onmessage (" + arrayBuffer.byteLength + " bytes)");
- array = JoinArrays(array, new Uint8Array(arrayBuffer));
+ // console.debug("adb_ws: onmessage (" + arrayBuffer.byteLength + " bytes)");
+ array = JoinArrays(array, new Uint8Array(arrayBuffer));
- while (array.length > 0) {
- if (array.length < 24) {
- // Incomplete package, must wait for more data.
- return;
- }
-
- let command = getU32LE(array, 0);
- let magic = getU32LE(array, 20);
-
- if (command != ((magic ^ 0xffffffff) >>> 0)) {
- console.log("command = " + command + ", magic = " + magic);
- console.log("adb message command vs magic failed.");
- return;
- }
-
- let payloadLength = getU32LE(array, 12);
-
- if (array.length < 24 + payloadLength) {
- // Incomplete package, must wait for more data.
- return;
- }
-
- let payloadChecksum = getU32LE(array, 16);
- let checksum = computeChecksum(array.slice(24));
-
- if (payloadChecksum != checksum) {
- console.log("adb message checksum mismatch.");
- // This can happen if a shell command executes while another
- // channel is receiving data.
- }
-
- switch (command) {
- case A_CNXN:
- {
- console.log("WebRTC adb connected.");
- connected();
- break;
- }
-
- case A_OKAY:
- {
- let remoteId = getU32LE(array, 4);
- console.log("WebRTC adb channel created w/ remoteId " + remoteId);
- connected();
- break;
- }
-
- case A_WRTE:
- {
- let remoteId = getU32LE(array, 4);
- adbSendOkay(remoteId);
- break;
- }
- }
- array = array.subarray(24 + payloadLength, array.length);
+ while (array.length > 0) {
+ if (array.length < 24) {
+ // Incomplete package, must wait for more data.
+ return;
}
+
+ let command = getU32LE(array, 0);
+ let magic = getU32LE(array, 20);
+
+ if (command != ((magic ^ 0xffffffff) >>> 0)) {
+ console.error('adb message command vs magic failed.');
+ console.error('command = ' + command + ', magic = ' + magic);
+ return;
+ }
+
+ let payloadLength = getU32LE(array, 12);
+
+ if (array.length < 24 + payloadLength) {
+ // Incomplete package, must wait for more data.
+ return;
+ }
+
+ let payloadChecksum = getU32LE(array, 16);
+ let checksum = computeChecksum(array.slice(24));
+
+ if (payloadChecksum != checksum) {
+ console.error('adb message checksum mismatch.');
+ // This can happen if a shell command executes while another
+ // channel is receiving data.
+ }
+
+ switch (command) {
+ case A_CNXN: {
+ console.info('WebRTC adb connected.');
+ connected();
+ break;
+ }
+
+ case A_OKAY: {
+ let remoteId = getU32LE(array, 4);
+ console.debug('WebRTC adb channel created w/ remoteId ' + remoteId);
+ connected();
+ break;
+ }
+
+ case A_WRTE: {
+ let remoteId = getU32LE(array, 4);
+ adbSendOkay(remoteId);
+ break;
+ }
+ }
+ array = array.subarray(24 + payloadLength, array.length);
+ }
}
function init_adb(devConn, ccb = connectedCb, dcb = disconnectedCb) {
- if (adb_ws) return;
+ if (adb_ws) return;
- adb_ws = {
- send: function(buffer) {
- devConn.sendAdbMessage(buffer);
- }
- };
- connectedCb = ccb;
- disconnectedCb = dcb;
- awaitConnection();
+ adb_ws = {
+ send: function(buffer) {
+ devConn.sendAdbMessage(buffer);
+ }
+ };
+ connectedCb = ccb;
+ disconnectedCb = dcb;
+ awaitConnection();
- devConn.onAdbMessage(msg => adbOnMessage(msg));
+ devConn.onAdbMessage(msg => adbOnMessage(msg));
- adbOpenConnection();
+ adbOpenConnection();
}
diff --git a/host/frontend/webrtc_operator/assets/js/app.js b/host/frontend/webrtc_operator/assets/js/app.js
index 53d0f79..4a5a1c9 100644
--- a/host/frontend/webrtc_operator/assets/js/app.js
+++ b/host/frontend/webrtc_operator/assets/js/app.js
@@ -16,450 +16,740 @@
'use strict';
-function ConnectToDevice(device_id) {
- console.log('ConnectToDevice ', device_id);
- const keyboardCaptureCtrl = document.getElementById('keyboard-capture-control');
- createToggleControl(keyboardCaptureCtrl, "keyboard", onKeyboardCaptureToggle);
- const micCaptureCtrl = document.getElementById('mic-capture-control');
- createToggleControl(micCaptureCtrl, "mic", onMicCaptureToggle);
- // TODO(b/163867676): Enable the microphone control when the audio stream is
- // injected into the guest. Until then, control is disabled.
- micCaptureCtrl.style.display = 'none';
+function showDeviceListUI() {
+ document.getElementById('error-message').style.display = 'none';
+ // Hide the device control screen
+ document.getElementById('device-connection').style.visibility = 'none';
+ // Show the device selection screen
+ document.getElementById('device-selector').style.display = 'visible';
+}
- const deviceScreen = document.getElementById('device-screen');
- const deviceAudio = document.getElementById('device-audio');
- const statusMessage = document.getElementById('status-message');
+function showDeviceControlUI() {
+ document.getElementById('error-message').style.display = 'none';
+ // Hide the device selection screen
+ document.getElementById('device-selector').style.display = 'none';
+ // Show the device control screen
+ document.getElementById('device-connection').style.visibility = 'visible';
+}
+function websocketUrl(path) {
+ return ((location.protocol == 'http:') ? 'ws:' : 'wss:') + location.host +
+ '/' + path;
+}
+
+async function ConnectDevice(deviceId) {
+ console.debug('Connect: ' + deviceId);
+ // Prepare messages in case of connection failure
let connectionAttemptDuration = 0;
- const intervalMs = 500;
- let deviceStatusEllipsisCount = 0;
- let animateDeviceStatusMessage = setInterval(function() {
+ const intervalMs = 15000;
+ let connectionInterval = setInterval(() => {
connectionAttemptDuration += intervalMs;
if (connectionAttemptDuration > 30000) {
- statusMessage.className = 'error';
- statusMessage.textContent = 'Connection should have occurred by now. ' +
- 'Please attempt to restart the guest device.';
- } else {
- if (connectionAttemptDuration > 15000) {
- statusMessage.textContent = 'Connection is taking longer than expected';
- } else {
- statusMessage.textContent = 'Connecting to device';
- }
- deviceStatusEllipsisCount = (deviceStatusEllipsisCount + 1) % 4;
- statusMessage.textContent += '.'.repeat(deviceStatusEllipsisCount);
+ showError(
+ 'Connection should have occurred by now. ' +
+ 'Please attempt to restart the guest device.');
+ clearInterval(connectionInterval);
+ } else if (connectionAttemptDuration > 15000) {
+ showWarning('Connection is taking longer than expected');
}
}, intervalMs);
- deviceScreen.addEventListener('loadeddata', (evt) => {
- clearInterval(animateDeviceStatusMessage);
- statusMessage.textContent = 'Awaiting bootup and adb connection. Please wait...';
- resizeDeviceView();
- deviceScreen.style.visibility = 'visible';
- // Enable the buttons after the screen is visible.
- for (const [_, button] of Object.entries(buttons)) {
- if (!button.adb) {
- button.button.disabled = false;
- }
+ let options = {
+ wsUrl: websocketUrl('connect_client'),
+ };
+
+ let module = await import('./cf_webrtc.js');
+ let deviceConnection = await module.Connect(deviceId, options);
+ console.info('Connected to ' + deviceId);
+ clearInterval(connectionInterval);
+ return deviceConnection;
+}
+
+function showWarning(msg) {
+ let element = document.getElementById('error-message');
+ element.className = 'warning';
+ element.textContent = msg;
+ element.style.visibility = 'visible';
+}
+
+function showError(msg) {
+ let element = document.getElementById('error-message');
+ element.className = 'error';
+ element.textContent = msg;
+ element.style.visibility = 'visible';
+}
+
+class DeviceListApp {
+ #websocketUrl;
+ #selectDeviceCb;
+
+ constructor({websocketUrl, selectDeviceCb}) {
+ this.#websocketUrl = websocketUrl;
+ this.#selectDeviceCb = selectDeviceCb;
+ }
+
+ start() {
+ // Get any devices that are already connected
+ this.#UpdateDeviceList();
+
+ // Update the list at the user's request
+ document.getElementById('refresh-list')
+ .addEventListener('click', evt => this.#UpdateDeviceList());
+ }
+
+ #UpdateDeviceList() {
+ let ws = new WebSocket(this.#websocketUrl);
+ ws.onopen = () => {
+ ws.send('give me those device ids');
+ };
+ ws.onmessage = msg => {
+ let device_ids = JSON.parse(msg.data);
+ this.#ShowNewDeviceList(device_ids);
+ };
+ }
+
+ #ShowNewDeviceList(device_ids) {
+ let ul = document.getElementById('device-list');
+ ul.innerHTML = '';
+ let count = 1;
+ let device_to_button_map = {};
+ for (const dev_id of device_ids) {
+ const button_id = 'connect_' + count++;
+ ul.innerHTML +=
+ ('<li class="device_entry" title="Connect to ' + dev_id +
+ '"><div><span>' + dev_id + '</span><button id="' + button_id +
+ '" >Connect</button></div></li>');
+ device_to_button_map[dev_id] = button_id;
}
- // Start the adb connection if it is not already started.
- initializeAdb();
- });
- let videoStream;
- let display_label;
- let buttons = {};
- let mouseIsDown = false;
- let deviceConnection;
- let touchIdSlotMap = new Map();
- let touchSlots = new Array();
+ for (const [dev_id, button_id] of Object.entries(device_to_button_map)) {
+ let button = document.getElementById(button_id);
+ button.addEventListener('click', evt => {
+ let button = $(evt.target);
+ let div = button.parent();
+ button.remove();
+ div.append('<span class="spinner material-icons">sync</span>');
+ this.#selectDeviceCb(dev_id);
+ });
+ }
+ }
+} // DeviceListApp
- let bootCompleted = false;
- let adbConnected = false;
- function showBootCompletion() {
- // Screen changed messages are not reported until after boot has completed.
- // Certain default adb buttons change screen state, so wait for boot
- // completion before enabling these buttons.
- if (adbConnected && bootCompleted) {
- statusMessage.className = 'connected';
- statusMessage.textContent =
- 'bootup and adb connection established successfully.';
- setTimeout(function() {
- statusMessage.style.visibility = 'hidden';
- }, 5000);
- for (const [_, button] of Object.entries(buttons)) {
- if (button.adb) {
- button.button.disabled = false;
+class DeviceDetailsUpdater {
+ #element;
+
+ constructor() {
+ this.#element = document.getElementById('device-details-hardware');
+ }
+
+ setHardwareDetailsText(text) {
+ this.#element.dataset.hardwareDetailsText = text;
+ return this;
+ }
+
+ setDeviceStateDetailsText(text) {
+ this.#element.dataset.deviceStateDetailsText = text;
+ return this;
+ }
+
+ setDisplayDetailsText(text) {
+ this.#element.dataset.displayDetailsText = text;
+ return this;
+ }
+
+ update() {
+ this.#element.textContent =
+ [
+ this.#element.dataset.hardwareDetailsText,
+ this.#element.dataset.deviceStateDetailsText,
+ this.#element.dataset.displayDetailsText,
+ ].filter(e => e /*remove empty*/)
+ .join('\n');
+ }
+} // DeviceDetailsUpdater
+
+class DeviceControlApp {
+ #deviceConnection = {};
+ #currentRotation = 0;
+ #displayDescriptions = [];
+ #buttons = {};
+
+ constructor(deviceConnection) {
+ this.#deviceConnection = deviceConnection;
+ }
+
+ start() {
+ console.debug('Device description: ', this.#deviceConnection.description);
+ this.#deviceConnection.onControlMessage(msg => this.#onControlMessage(msg));
+ let keyboardCaptureCtrl = createToggleControl(
+ document.getElementById('keyboard-capture-control'), 'keyboard');
+ let micCaptureCtrl = createToggleControl(
+ document.getElementById('mic-capture-control'), 'mic');
+
+ keyboardCaptureCtrl.OnClick(
+ enabled => this.#onKeyboardCaptureToggle(enabled));
+ micCaptureCtrl.OnClick(enabled => this.#onMicCaptureToggle(enabled));
+
+ this.#showDeviceUI();
+ }
+
+ #showDeviceUI() {
+ window.onresize = evt => this.#resizeDeviceDisplays();
+ // Set up control panel buttons
+ this.#buttons = {};
+ this.#buttons['power'] = createControlPanelButton(
+ 'power', 'Power', 'power_settings_new',
+ evt => this.#onControlPanelButton(evt));
+ this.#buttons['home'] = createControlPanelButton(
+ 'home', 'Home', 'home', evt => this.#onControlPanelButton(evt));
+ this.#buttons['menu'] = createControlPanelButton(
+ 'menu', 'Menu', 'menu', evt => this.#onControlPanelButton(evt));
+ this.#buttons['rotate'] = createControlPanelButton(
+ 'rotate', 'Rotate', 'screen_rotation',
+ evt => this.#onRotateButton(evt));
+ this.#buttons['rotate'].adb = true;
+ this.#buttons['volumemute'] = createControlPanelButton(
+ 'volumemute', 'Volume Mute', 'volume_mute',
+ evt => this.#onControlPanelButton(evt));
+ this.#buttons['volumedown'] = createControlPanelButton(
+ 'volumedown', 'Volume Down', 'volume_down',
+ evt => this.#onControlPanelButton(evt));
+ this.#buttons['volumeup'] = createControlPanelButton(
+ 'volumeup', 'Volume Up', 'volume_up',
+ evt => this.#onControlPanelButton(evt));
+ createModalButton(
+ 'device-details-button', 'device-details-modal',
+ 'device-details-close');
+ createModalButton(
+ 'bluetooth-console-button', 'bluetooth-console-modal',
+ 'bluetooth-console-close');
+
+ if (this.#deviceConnection.description.custom_control_panel_buttons.length >
+ 0) {
+ document.getElementById('control-panel-custom-buttons').style.display =
+ 'flex';
+ for (const button of this.#deviceConnection.description
+ .custom_control_panel_buttons) {
+ if (button.shell_command) {
+ // This button's command is handled by sending an ADB shell command.
+ this.#buttons[button.command] = createControlPanelButton(
+ button.command, button.title, button.icon_name,
+ e => this.#onCustomShellButton(button.shell_command, e),
+ 'control-panel-custom-buttons');
+ this.#buttons[button.command].adb = true;
+ } else if (button.device_states) {
+ // This button corresponds to variable hardware device state(s).
+ this.#buttons[button.command] = createControlPanelButton(
+ button.command, button.title, button.icon_name,
+ this.#getCustomDeviceStateButtonCb(button.device_states),
+ 'control-panel-custom-buttons');
+ for (const device_state of button.device_states) {
+ // hinge_angle is currently injected via an adb shell command that
+ // triggers a guest binary.
+ if ('hinge_angle_value' in device_state) {
+ this.#buttons[button.command].adb = true;
+ }
+ }
+ } else {
+ // This button's command is handled by custom action server.
+ this.#buttons[button.command] = createControlPanelButton(
+ button.command, button.title, button.icon_name,
+ evt => this.#onControlPanelButton(evt),
+ 'control-panel-custom-buttons');
}
}
}
+
+ // Set up displays
+ this.#createDeviceDisplays();
+
+ // Set up audio
+ const deviceAudio = document.getElementById('device-audio');
+ for (const audio_desc of this.#deviceConnection.description.audio_streams) {
+ let stream_id = audio_desc.stream_id;
+ this.#deviceConnection.getStream(stream_id)
+ .then(stream => {
+ deviceAudio.srcObject = stream;
+ })
+ .catch(e => console.error('Unable to get audio stream: ', e));
+ }
+
+ // Set up touch input
+ this.#startMouseTracking();
+
+ this.#updateDeviceHardwareDetails(
+ this.#deviceConnection.description.hardware);
+ this.#updateDeviceDisplayDetails(
+ this.#deviceConnection.description.displays[0]);
+
+ // Show the error message and disable buttons when the WebRTC connection
+ // fails.
+ this.#deviceConnection.onConnectionStateChange(state => {
+ if (state == 'disconnected' || state == 'failed') {
+ this.#showWebrtcError();
+ }
+ });
+
+ let bluetoothConsole =
+ cmdConsole('bluetooth-console-view', 'bluetooth-console-input');
+ bluetoothConsole.addCommandListener(cmd => {
+ let inputArr = cmd.split(' ');
+ let command = inputArr[0];
+ inputArr.shift();
+ let args = inputArr;
+ this.#deviceConnection.sendBluetoothMessage(
+ createRootcanalMessage(command, args));
+ });
+ this.#deviceConnection.onBluetoothMessage(msg => {
+ bluetoothConsole.addLine(decodeRootcanalMessage(msg));
+ });
}
- function initializeAdb() {
- init_adb(
- deviceConnection,
- function() {
- adbConnected = true;
- showBootCompletion();
- },
- function() {
- statusMessage.className = 'error';
- statusMessage.textContent = 'adb connection failed.';
- statusMessage.style.visibility = 'visible';
- for (const [_, button] of Object.entries(buttons)) {
- if (button.adb) {
- button.button.disabled = true;
- }
- }
- });
+ #showWebrtcError() {
+ document.getElementById('status-message').className = 'error';
+ document.getElementById('status-message').textContent =
+ 'No connection to the guest device. ' +
+ 'Please ensure the WebRTC process on the host machine is active.';
+ document.getElementById('status-message').style.visibility = 'visible';
+ const deviceDisplays = document.getElementById('device-displays');
+ deviceDisplays.style.display = 'none';
+ for (const [_, button] of Object.entries(this.#buttons)) {
+ button.disabled = true;
+ }
}
- let currentRotation = 0;
- let currentDisplayDetails;
- function onControlMessage(message) {
+ #takePhoto() {
+ const imageCapture = this.#deviceConnection.imageCapture;
+ if (imageCapture) {
+ const photoSettings = {
+ imageWidth: this.#deviceConnection.cameraWidth,
+ imageHeight: this.#deviceConnection.cameraHeight
+ };
+ imageCapture.takePhoto(photoSettings)
+ .then(blob => blob.arrayBuffer())
+ .then(buffer => this.#deviceConnection.sendOrQueueCameraData(buffer))
+ .catch(error => console.error(error));
+ }
+ }
+
+ #getCustomDeviceStateButtonCb(device_states) {
+ let states = device_states;
+ let index = 0;
+ return e => {
+ if (e.type == 'mousedown') {
+ // Reset any overridden device state.
+ adbShell('cmd device_state state reset');
+ // Send a device_state message for the current state.
+ let message = {
+ command: 'device_state',
+ ...states[index],
+ };
+ this.#deviceConnection.sendControlMessage(JSON.stringify(message));
+ console.debug('Control message sent: ', JSON.stringify(message));
+ let lidSwitchOpen = null;
+ if ('lid_switch_open' in states[index]) {
+ lidSwitchOpen = states[index].lid_switch_open;
+ }
+ let hingeAngle = null;
+ if ('hinge_angle_value' in states[index]) {
+ hingeAngle = states[index].hinge_angle_value;
+ // TODO(b/181157794): Use a custom Sensor HAL for hinge_angle
+ // injection instead of this guest binary.
+ adbShell(
+ '/vendor/bin/cuttlefish_sensor_injection hinge_angle ' +
+ states[index].hinge_angle_value);
+ }
+ // Update the Device Details view.
+ this.#updateDeviceStateDetails(lidSwitchOpen, hingeAngle);
+ // Cycle to the next state.
+ index = (index + 1) % states.length;
+ }
+ }
+ }
+
+ #resizeDeviceDisplays() {
+ const deviceDisplayPadding = 10;
+
+ let deviceDisplayList = document.getElementsByClassName('device-display');
+ let deviceDisplayVideoList =
+ document.getElementsByClassName('device-display-video');
+
+ const deviceDisplays = document.getElementById('device-displays');
+ const rotationDegrees = this.#getTransformRotation(deviceDisplays);
+ const rotationRadians = rotationDegrees * Math.PI / 180;
+
+ // Auto-scale the screen based on window size.
+ let availableWidth = deviceDisplays.clientWidth;
+ let availableHeight = deviceDisplays.clientHeight;
+
+ // Reserve space for padding between the displays.
+ availableWidth = availableWidth -
+ (this.#displayDescriptions.length * deviceDisplayPadding);
+
+ // Loop once over all of the displays to compute the total space needed.
+ let neededWidth = 0;
+ let neededHeight = 0;
+ for (let i = 0; i < deviceDisplayList.length; i++) {
+ let deviceDisplayDescription = this.#displayDescriptions[i];
+ let deviceDisplayVideo = deviceDisplayVideoList[i];
+
+ const originalDisplayWidth = deviceDisplayDescription.x_res;
+ const originalDisplayHeight = deviceDisplayDescription.y_res;
+
+ const neededBoundingBoxWidth =
+ Math.abs(Math.cos(rotationRadians) * originalDisplayWidth) +
+ Math.abs(Math.sin(rotationRadians) * originalDisplayHeight);
+ const neededBoundingBoxHeight =
+ Math.abs(Math.sin(rotationRadians) * originalDisplayWidth) +
+ Math.abs(Math.cos(rotationRadians) * originalDisplayHeight);
+
+ neededWidth = neededWidth + neededBoundingBoxWidth;
+ neededHeight = Math.max(neededHeight, neededBoundingBoxHeight);
+ }
+
+ const scaling =
+ Math.min(availableWidth / neededWidth, availableHeight / neededHeight);
+
+ // Loop again over all of the displays to set the sizes and positions.
+ let deviceDisplayLeftOffset = 0;
+ for (let i = 0; i < deviceDisplayList.length; i++) {
+ let deviceDisplay = deviceDisplayList[i];
+ let deviceDisplayVideo = deviceDisplayVideoList[i];
+ let deviceDisplayDescription = this.#displayDescriptions[i];
+
+ const originalDisplayWidth = deviceDisplayDescription.x_res;
+ const originalDisplayHeight = deviceDisplayDescription.y_res;
+
+ const scaledDisplayWidth = originalDisplayWidth * scaling;
+ const scaledDisplayHeight = originalDisplayHeight * scaling;
+
+ const neededBoundingBoxWidth =
+ Math.abs(Math.cos(rotationRadians) * originalDisplayWidth) +
+ Math.abs(Math.sin(rotationRadians) * originalDisplayHeight);
+ const neededBoundingBoxHeight =
+ Math.abs(Math.sin(rotationRadians) * originalDisplayWidth) +
+ Math.abs(Math.cos(rotationRadians) * originalDisplayHeight);
+
+ const scaledBoundingBoxWidth = neededBoundingBoxWidth * scaling;
+ const scaledBoundingBoxHeight = neededBoundingBoxHeight * scaling;
+
+ const offsetX = (scaledBoundingBoxWidth - scaledDisplayWidth) / 2;
+ const offsetY = (scaledBoundingBoxHeight - scaledDisplayHeight) / 2;
+
+ deviceDisplayVideo.style.width = scaledDisplayWidth;
+ deviceDisplayVideo.style.height = scaledDisplayHeight;
+ deviceDisplayVideo.style.transform = `translateX(${offsetX}px) ` +
+ `translateY(${offsetY}px) ` +
+ `rotateZ(${rotationDegrees}deg) `;
+
+ deviceDisplay.style.left = `${deviceDisplayLeftOffset}px`;
+ deviceDisplay.style.width = scaledBoundingBoxWidth;
+ deviceDisplay.style.height = scaledBoundingBoxHeight;
+
+ deviceDisplayLeftOffset = deviceDisplayLeftOffset + deviceDisplayPadding +
+ scaledBoundingBoxWidth;
+ }
+ }
+
+ #getTransformRotation(element) {
+ if (!element.style.textIndent) {
+ return 0;
+ }
+ // Remove 'px' and convert to float.
+ return parseFloat(element.style.textIndent.slice(0, -2));
+ }
+
+ #onControlMessage(message) {
let message_data = JSON.parse(message.data);
- console.log(message_data)
+ console.debug('Control message received: ', message_data)
let metadata = message_data.metadata;
if (message_data.event == 'VIRTUAL_DEVICE_BOOT_STARTED') {
// Start the adb connection after receiving the BOOT_STARTED message.
// (This is after the adbd start message. Attempting to connect
// immediately after adbd starts causes issues.)
- initializeAdb();
- }
- if (message_data.event == 'VIRTUAL_DEVICE_BOOT_COMPLETED') {
- bootCompleted = true;
- showBootCompletion();
+ this.#initializeAdb();
}
if (message_data.event == 'VIRTUAL_DEVICE_SCREEN_CHANGED') {
- if (metadata.rotation != currentRotation) {
+ if (metadata.rotation != this.#currentRotation) {
// Animate the screen rotation.
- deviceScreen.style.transition = 'transform 1s';
- } else {
- // Don't animate screen resizes, since these appear as odd sliding
- // animations if the screen is rotated due to the translateY.
- deviceScreen.style.transition = '';
+ const targetRotation = metadata.rotation == 0 ? 0 : -90;
+
+ $('#device-displays')
+ .animate(
+ {
+ textIndent: targetRotation,
+ },
+ {
+ duration: 1000,
+ step: (now, tween) => {
+ this.#resizeDeviceDisplays();
+ },
+ });
}
- currentRotation = metadata.rotation;
- updateDeviceDisplayDetails({
- dpi: metadata.dpi,
- x_res: metadata.width,
- y_res: metadata.height
- });
-
- resizeDeviceView();
+ this.#currentRotation = metadata.rotation;
+ this.#updateDeviceDisplayDetails(
+ {dpi: metadata.dpi, x_res: metadata.width, y_res: metadata.height});
}
- }
-
- const screensDiv = document.getElementById('screens');
- function resizeDeviceView() {
- // Auto-scale the screen based on window size.
- // Max window width of 70%, allowing space for the control panel.
- let ww = screensDiv.offsetWidth * 0.7;
- let wh = screensDiv.offsetHeight;
- let vw = currentDisplayDetails.x_res;
- let vh = currentDisplayDetails.y_res;
- let scaling = vw * wh > vh * ww ? ww / vw : wh / vh;
- if (currentRotation == 0) {
- deviceScreen.style.transform = null;
- deviceScreen.style.width = vw * scaling;
- deviceScreen.style.height = vh * scaling;
- } else if (currentRotation == 1) {
- deviceScreen.style.transform =
- `rotateZ(-90deg) translateY(-${vh * scaling}px)`;
- // When rotated, w and h are swapped.
- deviceScreen.style.width = vh * scaling;
- deviceScreen.style.height = vw * scaling;
- }
- }
- window.onresize = resizeDeviceView;
-
- function createControlPanelButton(command, title, icon_name,
- listener=onControlPanelButton,
- parent_id='control-panel-default-buttons') {
- let button = document.createElement('button');
- document.getElementById(parent_id).appendChild(button);
- button.title = title;
- button.dataset.command = command;
- button.disabled = true;
- // Capture mousedown/up/out commands instead of click to enable
- // hold detection. mouseout is used to catch if the user moves the
- // mouse outside the button while holding down.
- button.addEventListener('mousedown', listener);
- button.addEventListener('mouseup', listener);
- button.addEventListener('mouseout', listener);
- // Set the button image using Material Design icons.
- // See http://google.github.io/material-design-icons
- // and https://material.io/resources/icons
- button.classList.add('material-icons');
- button.innerHTML = icon_name;
- buttons[command] = { 'button': button }
- return buttons[command];
- }
- createControlPanelButton('power', 'Power', 'power_settings_new');
- createControlPanelButton('home', 'Home', 'home');
- createControlPanelButton('menu', 'Menu', 'menu');
- createControlPanelButton('rotate', 'Rotate', 'screen_rotation', onRotateButton);
- buttons['rotate'].adb = true;
- createControlPanelButton('volumemute', 'Volume Mute', 'volume_mute');
- createControlPanelButton('volumedown', 'Volume Down', 'volume_down');
- createControlPanelButton('volumeup', 'Volume Up', 'volume_up');
-
- const deviceDetailsModal = document.getElementById('device-details-modal');
- const deviceDetailsButton = document.getElementById('device-details-button');
- const deviceDetailsClose = document.getElementById('device-details-close');
- function showHideDeviceDetailsModal(show) {
- // Position the modal to the right of the device details button.
- deviceDetailsModal.style.top = deviceDetailsButton.offsetTop;
- deviceDetailsModal.style.left = deviceDetailsButton.offsetWidth + 30;
- if (show) {
- deviceDetailsModal.style.display = 'block';
- } else {
- deviceDetailsModal.style.display = 'none';
- }
- }
- // Allow the device details button to toggle the modal,
- deviceDetailsButton.addEventListener('click',
- evt => showHideDeviceDetailsModal(deviceDetailsModal.style.display != 'block'));
- // but the close button always closes.
- deviceDetailsClose.addEventListener('click',
- evt => showHideDeviceDetailsModal(false));
-
- let options = {
- wsUrl: ((location.protocol == 'http:') ? 'ws://' : 'wss://') +
- location.host + '/connect_client',
- };
-
- function showWebrtcError() {
- statusMessage.className = 'error';
- statusMessage.textContent = 'No connection to the guest device. ' +
- 'Please ensure the WebRTC process on the host machine is active.';
- statusMessage.style.visibility = 'visible';
- deviceScreen.style.display = 'none';
- for (const [_, button] of Object.entries(buttons)) {
- button.button.disabled = true;
- }
- }
-
- import('./cf_webrtc.js')
- .then(webrtcModule => webrtcModule.Connect(device_id, options))
- .then(devConn => {
- deviceConnection = devConn;
- // TODO(b/143667633): get multiple display configuration from the
- // description object
- console.log(deviceConnection.description);
- let stream_id = devConn.description.displays[0].stream_id;
- devConn.getStream(stream_id).then(stream => {
- videoStream = stream;
- display_label = stream_id;
- deviceScreen.srcObject = videoStream;
- }).catch(e => console.error('Unable to get display stream: ', e));
- for (const audio_desc of devConn.description.audio_streams) {
- let stream_id = audio_desc.stream_id;
- devConn.getStream(stream_id).then(stream => {
- deviceAudio.srcObject = stream;
- }).catch(e => console.error('Unable to get audio stream: ', e));
+ if (message_data.event == 'VIRTUAL_DEVICE_CAPTURE_IMAGE') {
+ if (this.$deviceConnection.cameraEnabled) {
+ this.#takePhoto();
}
- startMouseTracking(); // TODO stopMouseTracking() when disconnected
- updateDeviceHardwareDetails(deviceConnection.description.hardware);
- updateDeviceDisplayDetails(deviceConnection.description.displays[0]);
- if (deviceConnection.description.custom_control_panel_buttons.length > 0) {
- document.getElementById('control-panel-custom-buttons').style.display = 'flex';
- for (const button of deviceConnection.description.custom_control_panel_buttons) {
- if (button.shell_command) {
- // This button's command is handled by sending an ADB shell command.
- createControlPanelButton(button.command, button.title, button.icon_name,
- e => onCustomShellButton(button.shell_command, e),
- 'control-panel-custom-buttons');
- buttons[button.command].adb = true;
- } else {
- // This button's command is handled by custom action server.
- createControlPanelButton(button.command, button.title, button.icon_name,
- onControlPanelButton,
- 'control-panel-custom-buttons');
- }
- }
- }
- deviceConnection.onControlMessage(msg => onControlMessage(msg));
- // Start the screen as hidden. Only show when data is ready.
- deviceScreen.style.visibility = 'hidden';
- // Send an initial home button press when WebRTC connects. This is needed
- // so that the device screen receives an initial frame even if WebRTC is
- // connected long after the device boots up.
- deviceConnection.sendControlMessage(JSON.stringify({
- command: 'home',
- state: 'down',
- }));
- deviceConnection.sendControlMessage(JSON.stringify({
- command: 'home',
- state: 'up',
- }));
- // Show the error message and disable buttons when the WebRTC connection fails.
- deviceConnection.onConnectionStateChange(state => {
- if (state == 'disconnected' || state == 'failed') {
- showWebrtcError();
- }
- });
- }, rejection => {
- console.error('Unable to connect: ', rejection);
- showWebrtcError();
- });
-
- let hardwareDetailsText = '';
- let displayDetailsText = '';
- function updateDeviceDetailsText() {
- document.getElementById('device-details-hardware').textContent = [
- hardwareDetailsText,
- displayDetailsText,
- ].join('\n');
+ }
}
- function updateDeviceHardwareDetails(hardware) {
+
+ #updateDeviceStateDetails(lidSwitchOpen, hingeAngle) {
+ let deviceStateDetailsTextLines = [];
+ if (lidSwitchOpen != null) {
+ let state = lidSwitchOpen ? 'Opened' : 'Closed';
+ deviceStateDetailsTextLines.push(`Lid Switch - ${state}`);
+ }
+ if (hingeAngle != null) {
+ deviceStateDetailsTextLines.push(`Hinge Angle - ${hingeAngle}`);
+ }
+ let deviceStateDetailsText = deviceStateDetailsTextLines.join('\n');
+ new DeviceDetailsUpdater()
+ .setDeviceStateDetailsText(deviceStateDetailsText)
+ .update();
+ }
+
+ #updateDeviceHardwareDetails(hardware) {
let hardwareDetailsTextLines = [];
- Object.keys(hardware).forEach(function(key) {
+ Object.keys(hardware).forEach((key) => {
let value = hardware[key];
hardwareDetailsTextLines.push(`${key} - ${value}`);
});
- hardwareDetailsText = hardwareDetailsTextLines.join('\n');
- updateDeviceDetailsText();
+ let hardwareDetailsText = hardwareDetailsTextLines.join('\n');
+ new DeviceDetailsUpdater()
+ .setHardwareDetailsText(hardwareDetailsText)
+ .update();
}
- function updateDeviceDisplayDetails(display) {
- currentDisplayDetails = display;
+
+ #updateDeviceDisplayDetails(display) {
let dpi = display.dpi;
let x_res = display.x_res;
let y_res = display.y_res;
- let rotated = currentRotation == 1 ? ' (Rotated)' : '';
- displayDetailsText = `Display - ${x_res}x${y_res} (${dpi}DPI)${rotated}`;
- updateDeviceDetailsText();
+ let rotated = this.#currentRotation == 1 ? ' (Rotated)' : '';
+ let displayDetailsText =
+ `Display - ${x_res}x${y_res} (${dpi}DPI)${rotated}`;
+ new DeviceDetailsUpdater()
+ .setDisplayDetailsText(displayDetailsText)
+ .update();
}
- function onKeyboardCaptureToggle(enabled) {
- if (enabled) {
- startKeyboardTracking();
- } else {
- stopKeyboardTracking();
+ // Creates a <video> element and a <div> container element for each display.
+ // The extra <div> container elements are used to maintain the width and
+ // height of the device as the CSS 'transform' property used on the <video>
+ // element for rotating the device only affects the visuals of the element
+ // and not its layout.
+ #createDeviceDisplays() {
+ console.debug(
+ 'Display descriptions: ', this.#deviceConnection.description.displays);
+ this.#displayDescriptions = this.#deviceConnection.description.displays;
+ let anyDisplayLoaded = false;
+ const deviceDisplays = document.getElementById('device-displays');
+ for (const deviceDisplayDescription of this.#displayDescriptions) {
+ let deviceDisplay = document.createElement('div');
+ deviceDisplay.classList.add('device-display');
+ // Start the screen as hidden. Only show when data is ready.
+ deviceDisplay.style.visibility = 'hidden';
+
+ let deviceDisplayVideo = document.createElement('video');
+ deviceDisplayVideo.autoplay = true;
+ deviceDisplayVideo.id = deviceDisplayDescription.stream_id;
+ deviceDisplayVideo.classList.add('device-display-video');
+
+ deviceDisplayVideo.addEventListener('loadeddata', (evt) => {
+ if (!anyDisplayLoaded) {
+ anyDisplayLoaded = true;
+ this.#onDeviceDisplayLoaded();
+ }
+ });
+
+ deviceDisplay.appendChild(deviceDisplayVideo);
+ deviceDisplays.appendChild(deviceDisplay);
+
+ let stream_id = deviceDisplayDescription.stream_id;
+ this.#deviceConnection.getStream(stream_id)
+ .then(stream => {
+ deviceDisplayVideo.srcObject = stream;
+ })
+ .catch(e => console.error('Unable to get display stream: ', e));
}
}
- function onMicCaptureToggle(enabled) {
- deviceConnection.useMic(enabled);
+ #initializeAdb() {
+ init_adb(
+ this.#deviceConnection, () => this.#showAdbConnected(),
+ () => this.#showAdbError());
}
- function onControlPanelButton(e) {
+ #showAdbConnected() {
+ // Screen changed messages are not reported until after boot has completed.
+ // Certain default adb buttons change screen state, so wait for boot
+ // completion before enabling these buttons.
+ document.getElementById('status-message').className = 'connected';
+ document.getElementById('status-message').textContent =
+ 'adb connection established successfully.';
+ setTimeout(() => {
+ document.getElementById('status-message').style.visibility = 'hidden';
+ }, 5000);
+ for (const [_, button] of Object.entries(this.#buttons)) {
+ if (button.adb) {
+ button.disabled = false;
+ }
+ }
+ }
+
+ #showAdbError() {
+ document.getElementById('status-message').className = 'error';
+ document.getElementById('status-message').textContent =
+ 'adb connection failed.';
+ document.getElementById('status-message').style.visibility = 'visible';
+ for (const [_, button] of Object.entries(this.#buttons)) {
+ if (button.adb) {
+ button.disabled = true;
+ }
+ }
+ }
+
+ #onDeviceDisplayLoaded() {
+ document.getElementById('status-message').textContent =
+ 'Awaiting bootup and adb connection. Please wait...';
+ this.#resizeDeviceDisplays();
+
+ let deviceDisplayList = document.getElementsByClassName('device-display');
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.style.visibility = 'visible';
+ }
+
+ // Enable the buttons after the screen is visible.
+ for (const [key, button] of Object.entries(this.#buttons)) {
+ if (!button.adb) {
+ button.disabled = false;
+ }
+ }
+ // Start the adb connection if it is not already started.
+ this.#initializeAdb();
+ }
+
+ #onRotateButton(e) {
+ // Attempt to init adb again, in case the initial connection failed.
+ // This succeeds immediately if already connected.
+ this.#initializeAdb();
+ if (e.type == 'mousedown') {
+ adbShell(
+ '/vendor/bin/cuttlefish_sensor_injection rotate ' +
+ (this.#currentRotation == 0 ? 'landscape' : 'portrait'))
+ }
+ }
+
+ #onControlPanelButton(e) {
if (e.type == 'mouseout' && e.which == 0) {
// Ignore mouseout events if no mouse button is pressed.
return;
}
- deviceConnection.sendControlMessage(JSON.stringify({
+ this.#deviceConnection.sendControlMessage(JSON.stringify({
command: e.target.dataset.command,
- state: e.type == 'mousedown' ? "down" : "up",
+ button_state: e.type == 'mousedown' ? 'down' : 'up',
}));
}
- function onRotateButton(e) {
- // Attempt to init adb again, in case the initial connection failed.
- // This succeeds immediately if already connected.
- initializeAdb();
- if (e.type == 'mousedown') {
- adbShell(
- '/vendor/bin/cuttlefish_rotate ' +
- (currentRotation == 0 ? 'landscape' : 'portrait'))
- }
- }
- function onCustomShellButton(shell_command, e) {
- // Attempt to init adb again, in case the initial connection failed.
- // This succeeds immediately if already connected.
- initializeAdb();
- if (e.type == 'mousedown') {
- adbShell(shell_command);
+ #onKeyboardCaptureToggle(enabled) {
+ if (enabled) {
+ document.addEventListener('keydown', evt => this.#onKeyEvent(evt));
+ document.addEventListener('keyup', evt => this.#onKeyEvent(evt));
+ } else {
+ document.removeEventListener('keydown', evt => this.#onKeyEvent(evt));
+ document.removeEventListener('keyup', evt => this.#onKeyEvent(evt));
}
}
- function startMouseTracking() {
+ #onKeyEvent(e) {
+ e.preventDefault();
+ this.#deviceConnection.sendKeyEvent(e.code, e.type);
+ }
+
+ #startMouseTracking() {
+ let $this = this;
+ let mouseIsDown = false;
+ let mouseCtx = {
+ down: false,
+ touchIdSlotMap: new Map(),
+ touchSlots: [],
+ };
+ function onStartDrag(e) {
+ e.preventDefault();
+
+ // console.debug("mousedown at " + e.pageX + " / " + e.pageY);
+ mouseCtx.down = true;
+
+ $this.#sendEventUpdate(mouseCtx, e);
+ }
+
+ function onEndDrag(e) {
+ e.preventDefault();
+
+ // console.debug("mouseup at " + e.pageX + " / " + e.pageY);
+ mouseCtx.down = false;
+
+ $this.#sendEventUpdate(mouseCtx, e);
+ }
+
+ function onContinueDrag(e) {
+ e.preventDefault();
+
+ // console.debug("mousemove at " + e.pageX + " / " + e.pageY + ", down=" +
+ // mouseIsDown);
+ if (mouseCtx.down) {
+ $this.#sendEventUpdate(mouseCtx, e);
+ }
+ }
+
+ let deviceDisplayList = document.getElementsByClassName('device-display');
if (window.PointerEvent) {
- deviceScreen.addEventListener('pointerdown', onStartDrag);
- deviceScreen.addEventListener('pointermove', onContinueDrag);
- deviceScreen.addEventListener('pointerup', onEndDrag);
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.addEventListener('pointerdown', onStartDrag);
+ deviceDisplay.addEventListener('pointermove', onContinueDrag);
+ deviceDisplay.addEventListener('pointerup', onEndDrag);
+ }
} else if (window.TouchEvent) {
- deviceScreen.addEventListener('touchstart', onStartDrag);
- deviceScreen.addEventListener('touchmove', onContinueDrag);
- deviceScreen.addEventListener('touchend', onEndDrag);
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.addEventListener('touchstart', onStartDrag);
+ deviceDisplay.addEventListener('touchmove', onContinueDrag);
+ deviceDisplay.addEventListener('touchend', onEndDrag);
+ }
} else if (window.MouseEvent) {
- deviceScreen.addEventListener('mousedown', onStartDrag);
- deviceScreen.addEventListener('mousemove', onContinueDrag);
- deviceScreen.addEventListener('mouseup', onEndDrag);
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.addEventListener('mousedown', onStartDrag);
+ deviceDisplay.addEventListener('mousemove', onContinueDrag);
+ deviceDisplay.addEventListener('mouseup', onEndDrag);
+ }
}
}
- function stopMouseTracking() {
- if (window.PointerEvent) {
- deviceScreen.removeEventListener('pointerdown', onStartDrag);
- deviceScreen.removeEventListener('pointermove', onContinueDrag);
- deviceScreen.removeEventListener('pointerup', onEndDrag);
- } else if (window.TouchEvent) {
- deviceScreen.removeEventListener('touchstart', onStartDrag);
- deviceScreen.removeEventListener('touchmove', onContinueDrag);
- deviceScreen.removeEventListener('touchend', onEndDrag);
- } else if (window.MouseEvent) {
- deviceScreen.removeEventListener('mousedown', onStartDrag);
- deviceScreen.removeEventListener('mousemove', onContinueDrag);
- deviceScreen.removeEventListener('mouseup', onEndDrag);
- }
- }
+ #sendEventUpdate(ctx, e) {
+ let eventType = e.type.substring(0, 5);
- function startKeyboardTracking() {
- document.addEventListener('keydown', onKeyEvent);
- document.addEventListener('keyup', onKeyEvent);
- }
-
- function stopKeyboardTracking() {
- document.removeEventListener('keydown', onKeyEvent);
- document.removeEventListener('keyup', onKeyEvent);
- }
-
- function onStartDrag(e) {
- e.preventDefault();
-
- // console.log("mousedown at " + e.pageX + " / " + e.pageY);
- mouseIsDown = true;
-
- sendEventUpdate(true, e);
- }
-
- function onEndDrag(e) {
- e.preventDefault();
-
- // console.log("mouseup at " + e.pageX + " / " + e.pageY);
- mouseIsDown = false;
-
- sendEventUpdate(false, e);
- }
-
- function onContinueDrag(e) {
- e.preventDefault();
-
- // console.log("mousemove at " + e.pageX + " / " + e.pageY + ", down=" +
- // mouseIsDown);
- if (mouseIsDown) {
- sendEventUpdate(true, e);
- }
- }
-
- function sendEventUpdate(down, e) {
- console.assert(deviceConnection, 'Can\'t send mouse update without device');
- var eventType = e.type.substring(0, 5);
+ // The <video> element:
+ const deviceDisplay = e.target;
// Before the first video frame arrives there is no way to know width and
// height of the device's screen, so turn every click into a click at 0x0.
// A click at that position is not more dangerous than anywhere else since
// the user is clicking blind anyways.
- const videoWidth = deviceScreen.videoWidth? deviceScreen.videoWidth: 1;
- const videoHeight = deviceScreen.videoHeight? deviceScreen.videoHeight: 1;
- const elementWidth = deviceScreen.offsetWidth? deviceScreen.offsetWidth: 1;
- const elementHeight = deviceScreen.offsetHeight? deviceScreen.offsetHeight: 1;
+ const videoWidth = deviceDisplay.videoWidth ? deviceDisplay.videoWidth : 1;
+ const videoHeight =
+ deviceDisplay.videoHeight ? deviceDisplay.videoHeight : 1;
+ const elementWidth =
+ deviceDisplay.offsetWidth ? deviceDisplay.offsetWidth : 1;
+ const elementHeight =
+ deviceDisplay.offsetHeight ? deviceDisplay.offsetHeight : 1;
// vh*ew > eh*vw? then scale h instead of w
const scaleHeight = videoHeight * elementWidth > videoWidth * elementHeight;
- var elementScaling = 0, videoScaling = 0;
+ let elementScaling = 0, videoScaling = 0;
if (scaleHeight) {
elementScaling = elementHeight;
videoScaling = videoHeight;
@@ -492,35 +782,36 @@
// (which equals the video size here).
// - The sent ABS_X and ABS_Y values need to be scaled based on the
// ratio between the max size (video size) and in-browser size.
- const scaling = scaleWidth ? videoWidth / elementWidth : videoHeight / elementHeight;
+ const scaling =
+ scaleWidth ? videoWidth / elementWidth : videoHeight / elementHeight;
- var xArr = [];
- var yArr = [];
- var idArr = [];
- var slotArr = [];
+ let xArr = [];
+ let yArr = [];
+ let idArr = [];
+ let slotArr = [];
- if (eventType == "mouse" || eventType == "point") {
+ if (eventType == 'mouse' || eventType == 'point') {
xArr.push(e.offsetX);
yArr.push(e.offsetY);
let thisId = -1;
- if (eventType == "point") {
+ if (eventType == 'point') {
thisId = e.pointerId;
}
slotArr.push(0);
idArr.push(thisId);
- } else if (eventType == "touch") {
+ } else if (eventType == 'touch') {
// touchstart: list of touch points that became active
// touchmove: list of touch points that changed
// touchend: list of touch points that were removed
let changes = e.changedTouches;
let rect = e.target.getBoundingClientRect();
- for (var i=0; i < changes.length; i++) {
+ for (let i = 0; i < changes.length; i++) {
xArr.push(changes[i].pageX - rect.left);
yArr.push(changes[i].pageY - rect.top);
- if (touchIdSlotMap.has(changes[i].identifier)) {
- let slot = touchIdSlotMap.get(changes[i].identifier);
+ if (ctx.touchIdSlotMap.has(changes[i].identifier)) {
+ let slot = ctx.touchIdSlotMap.get(changes[i].identifier);
slotArr.push(slot);
if (e.type == 'touchstart') {
@@ -530,26 +821,26 @@
} else if (e.type == 'touchmove') {
idArr.push(changes[i].identifier);
} else if (e.type == 'touchend') {
- touchSlots[slot] = false;
- touchIdSlotMap.delete(changes[i].identifier);
+ ctx.touchSlots[slot] = false;
+ ctx.touchIdSlotMap.delete(changes[i].identifier);
idArr.push(-1);
}
} else {
if (e.type == 'touchstart') {
let slot = -1;
- for (var j=0; j < touchSlots.length; j++) {
- if (!touchSlots[j]) {
+ for (let j = 0; j < ctx.touchSlots.length; j++) {
+ if (!ctx.touchSlots[j]) {
slot = j;
break;
}
}
if (slot == -1) {
- slot = touchSlots.length;
- touchSlots.push(true);
+ slot = ctx.touchSlots.length;
+ ctx.touchSlots.push(true);
}
slotArr.push(slot);
- touchSlots[slot] = true;
- touchIdSlotMap.set(changes[i].identifier, slot);
+ ctx.touchSlots[slot] = true;
+ ctx.touchIdSlotMap.set(changes[i].identifier, slot);
idArr.push(changes[i].identifier);
} else if (e.type == 'touchmove') {
// error
@@ -564,11 +855,12 @@
}
}
- for (var i=0; i < xArr.length; i++) {
+ for (let i = 0; i < xArr.length; i++) {
xArr[i] = xArr[i] * scaling;
yArr[i] = yArr[i] * scaling;
- // Substract the offset produced by the difference in aspect ratio, if any.
+ // Substract the offset produced by the difference in aspect ratio, if
+ // any.
if (scaleWidth) {
// Width was scaled, leaving excess content height, so subtract from y.
yArr[i] -= (elementHeight * scaling - videoHeight) / 2;
@@ -584,62 +876,48 @@
// NOTE: Rotation is handled automatically because the CSS rotation through
// transforms also rotates the coordinates of events on the object.
- deviceConnection.sendMultiTouch(
- {idArr, xArr, yArr, down, slotArr, display_label});
+ const display_label = deviceDisplay.id;
+
+ this.#deviceConnection.sendMultiTouch(
+ {idArr, xArr, yArr, down: ctx.down, slotArr, display_label});
}
- function onKeyEvent(e) {
- e.preventDefault();
- console.assert(deviceConnection, 'Can\'t send key event without device');
- deviceConnection.sendKeyEvent(e.code, e.type);
- }
-}
-
-/******************************************************************************/
-
-function ConnectDeviceCb(dev_id) {
- console.log('Connect: ' + dev_id);
- // Hide the device selection screen
- document.getElementById('device-selector').style.display = 'none';
- // Show the device control screen
- document.getElementById('device-connection').style.visibility = 'visible';
- ConnectToDevice(dev_id);
-}
-
-function ShowNewDeviceList(device_ids) {
- let ul = document.getElementById('device-list');
- ul.innerHTML = "";
- let count = 1;
- let device_to_button_map = {};
- for (const dev_id of device_ids) {
- const button_id = 'connect_' + count++;
- ul.innerHTML += ('<li class="device_entry" title="Connect to ' + dev_id
- + '">' + dev_id + '<button id="' + button_id
- + '" >Connect</button></li>');
- device_to_button_map[dev_id] = button_id;
+ #onMicCaptureToggle(enabled) {
+ this.#deviceConnection.useMic(enabled);
}
- for (const [dev_id, button_id] of Object.entries(device_to_button_map)) {
- document.getElementById(button_id).addEventListener(
- 'click', evt => ConnectDeviceCb(dev_id));
+ #onVideoCaptureToggle(enabled) {
+ this.#deviceConnection.useVideo(enabled);
}
-}
-function UpdateDeviceList() {
- let url = ((location.protocol == 'http:') ? 'ws:' : 'wss:') + location.host +
- '/list_devices';
- let ws = new WebSocket(url);
- ws.onopen = () => {
- ws.send("give me those device ids");
- };
- ws.onmessage = msg => {
- let device_ids = JSON.parse(msg.data);
- ShowNewDeviceList(device_ids);
- };
-}
+ #onCustomShellButton(shell_command, e) {
+ // Attempt to init adb again, in case the initial connection failed.
+ // This succeeds immediately if already connected.
+ this.#initializeAdb();
+ if (e.type == 'mousedown') {
+ adbShell(shell_command);
+ }
+ }
+} // DeviceControlApp
-// Get any devices that are already connected
-UpdateDeviceList();
-// Update the list at the user's request
-document.getElementById('refresh-list')
- .addEventListener('click', evt => UpdateDeviceList());
+
+// The app starts by showing the device list
+showDeviceListUI();
+let listDevicesUrl = websocketUrl('list_devices');
+let selectDeviceCb = deviceId => {
+ ConnectDevice(deviceId).then(
+ deviceConnection => {
+ let deviceControlApp = new DeviceControlApp(deviceConnection);
+ deviceControlApp.start();
+ showDeviceControlUI();
+ },
+ err => {
+ console.error('Unable to connect: ', err);
+ showError(
+ 'No connection to the guest device. ' +
+ 'Please ensure the WebRTC process on the host machine is active.');
+ });
+};
+let deviceListApp =
+ new DeviceListApp({websocketUrl: listDevicesUrl, selectDeviceCb});
+deviceListApp.start();
diff --git a/host/frontend/webrtc_operator/assets/js/cf_webrtc.js b/host/frontend/webrtc_operator/assets/js/cf_webrtc.js
index ed7fc37..ad045f0 100644
--- a/host/frontend/webrtc_operator/assets/js/cf_webrtc.js
+++ b/host/frontend/webrtc_operator/assets/js/cf_webrtc.js
@@ -15,7 +15,7 @@
*/
function createDataChannel(pc, label, onMessage) {
- console.log('creating data channel: ' + label);
+ console.debug('creating data channel: ' + label);
let dataChannel = pc.createDataChannel(label);
// Return an object with a send function like that of the dataChannel, but
// that only actually sends over the data channel once it has connected.
@@ -25,11 +25,11 @@
resolve(dataChannel);
};
dataChannel.onclose = () => {
- console.log(
+ console.debug(
'Data channel=' + label + ' state=' + dataChannel.readyState);
};
dataChannel.onmessage = onMessage ? onMessage : (msg) => {
- console.log('Data channel=' + label + ' data="' + msg.data + '"');
+ console.debug('Data channel=' + label + ' data="' + msg.data + '"');
};
dataChannel.onerror = err => {
reject(err);
@@ -45,7 +45,7 @@
}
function awaitDataChannel(pc, label, onMessage) {
- console.log('expecting data channel: ' + label);
+ console.debug('expecting data channel: ' + label);
// Return an object with a send function like that of the dataChannel, but
// that only actually sends over the data channel once it has connected.
return {
@@ -58,11 +58,11 @@
resolve(dataChannel);
};
dataChannel.onclose = () => {
- console.log(
+ console.debug(
'Data channel=' + label + ' state=' + dataChannel.readyState);
};
dataChannel.onmessage = onMessage ? onMessage : (msg) => {
- console.log('Data channel=' + label + ' data="' + msg.data + '"');
+ console.debug('Data channel=' + label + ' data="' + msg.data + '"');
};
dataChannel.onerror = err => {
reject(err);
@@ -82,12 +82,22 @@
}
class DeviceConnection {
- constructor(pc, control, audio_stream) {
+ constructor(pc, control, media_stream) {
this._pc = pc;
this._control = control;
- this._audio_stream = audio_stream;
+ this._media_stream = media_stream;
// Disable the microphone by default
this.useMic(false);
+ this.useVideo(false);
+ this._cameraDataChannel = pc.createDataChannel('camera-data-channel');
+ this._cameraDataChannel.binaryType = 'arraybuffer';
+ this._cameraInputQueue = new Array();
+ var self = this;
+ this._cameraDataChannel.onbufferedamountlow = () => {
+ if (self._cameraInputQueue.length > 0) {
+ self.sendCameraData(self._cameraInputQueue.shift());
+ }
+ };
this._inputChannel = createDataChannel(pc, 'input-channel');
this._adbChannel = createDataChannel(pc, 'adb-channel', (msg) => {
if (this._onAdbMessage) {
@@ -103,11 +113,20 @@
console.error('Received unexpected Control message');
}
});
+ this._bluetoothChannel =
+ createDataChannel(pc, 'bluetooth-channel', (msg) => {
+ if (this._onBluetoothMessage) {
+ this._onBluetoothMessage(msg.data);
+ } else {
+ console.error('Received unexpected Bluetooth message');
+ }
+ });
+ this.sendCameraResolution();
this._streams = {};
this._streamPromiseResolvers = {};
pc.addEventListener('track', e => {
- console.log('Got remote stream: ', e);
+ console.debug('Got remote stream: ', e);
for (const stream of e.streams) {
this._streams[stream.id] = stream;
if (this._streamPromiseResolvers[stream.id]) {
@@ -128,6 +147,28 @@
return this._description;
}
+ get imageCapture() {
+ if (this._media_stream) {
+ const track = this._media_stream.getVideoTracks()[0];
+ return new ImageCapture(track);
+ }
+ return undefined;
+ }
+
+ get cameraWidth() {
+ return this._x_res;
+ }
+
+ get cameraHeight() {
+ return this._y_res;
+ }
+
+ get cameraEnabled() {
+ if (this._media_stream) {
+ return this._media_stream.getVideoTracks().some(track => track.enabled);
+ }
+ }
+
getStream(stream_id) {
return new Promise((resolve, reject) => {
if (this._streams[stream_id]) {
@@ -193,21 +234,73 @@
}
useMic(in_use) {
- if (this._audio_stream) {
- this._audio_stream.getTracks().forEach(track => track.enabled = in_use);
+ if (this._media_stream) {
+ this._media_stream.getAudioTracks().forEach(
+ track => track.enabled = in_use);
}
}
+ useVideo(in_use) {
+ if (this._media_stream) {
+ this._media_stream.getVideoTracks().forEach(
+ track => track.enabled = in_use);
+ }
+ }
+
+ sendCameraResolution() {
+ if (this._media_stream) {
+ const cameraTracks = this._media_stream.getVideoTracks();
+ if (cameraTracks.length > 0) {
+ const settings = cameraTracks[0].getSettings();
+ this._x_res = settings.width;
+ this._y_res = settings.height;
+ this.sendControlMessage(JSON.stringify({
+ command: 'camera_settings',
+ width: settings.width,
+ height: settings.height,
+ frame_rate: settings.frameRate,
+ facing: settings.facingMode
+ }));
+ }
+ }
+ }
+
+ sendOrQueueCameraData(data) {
+ if (this._cameraDataChannel.bufferedAmount > 0 ||
+ this._cameraInputQueue.length > 0) {
+ this._cameraInputQueue.push(data);
+ } else {
+ this.sendCameraData(data);
+ }
+ }
+
+ sendCameraData(data) {
+ const MAX_SIZE = 65535;
+ const END_MARKER = 'EOF';
+ for (let i = 0; i < data.byteLength; i += MAX_SIZE) {
+ // range is clamped to the valid index range
+ this._cameraDataChannel.send(data.slice(i, i + MAX_SIZE));
+ }
+ this._cameraDataChannel.send(END_MARKER);
+ }
+
// Provide a callback to receive control-related comms from the device
onControlMessage(cb) {
this._onControlMessage = cb;
}
+ sendBluetoothMessage(msg) {
+ this._bluetoothChannel.send(msg);
+ }
+
+ onBluetoothMessage(cb) {
+ this._onBluetoothMessage = cb;
+ }
+
// Provide a callback to receive connectionstatechange states.
onConnectionStateChange(cb) {
this._pc.addEventListener(
- 'connectionstatechange',
- evt => cb(this._pc.connectionState));
+ 'connectionstatechange', evt => cb(this._pc.connectionState));
}
}
@@ -232,7 +325,7 @@
this._wsPromise = new Promise((resolve, reject) => {
let ws = new WebSocket(wsUrl);
ws.onopen = () => {
- console.info(`Connected to ${wsUrl}`);
+ console.debug(`Connected to ${wsUrl}`);
resolve(ws);
};
ws.onerror = evt => {
@@ -272,7 +365,8 @@
break;
default:
console.error('Unrecognized message type from server: ', type);
- this._on_connection_failed('Unrecognized message type from server: ' + type);
+ this._on_connection_failed(
+ 'Unrecognized message type from server: ' + type);
console.error(message);
}
}
@@ -334,7 +428,7 @@
}
ConnectDevice() {
- console.log('ConnectDevice');
+ console.debug('ConnectDevice');
this._sendToDevice({type: 'request-offer'});
}
@@ -342,7 +436,7 @@
* Sends a remote description to the device.
*/
async sendClientDescription(desc) {
- console.log('sendClientDescription');
+ console.debug('sendClientDescription');
this._sendToDevice({type: 'answer', sdp: desc.sdp});
}
@@ -362,15 +456,15 @@
let pc = new RTCPeerConnection(pc_config);
pc.addEventListener('icecandidate', evt => {
- console.log('Local ICE Candidate: ', evt.candidate);
+ console.debug('Local ICE Candidate: ', evt.candidate);
});
pc.addEventListener('iceconnectionstatechange', evt => {
- console.log(`ICE State Change: ${pc.iceConnectionState}`);
+ console.debug(`ICE State Change: ${pc.iceConnectionState}`);
});
pc.addEventListener(
'connectionstatechange',
- evt =>
- console.log(`WebRTC Connection State Change: ${pc.connectionState}`));
+ evt => console.debug(
+ `WebRTC Connection State Change: ${pc.connectionState}`));
return pc;
}
@@ -379,8 +473,8 @@
let requestRet = await control.requestDevice(deviceId);
let deviceInfo = requestRet.deviceInfo;
let infraConfig = requestRet.infraConfig;
- console.log('Device available:');
- console.log(deviceInfo);
+ console.debug('Device available:');
+ console.debug(deviceInfo);
let pc_config = {iceServers: []};
if (infraConfig.ice_servers && infraConfig.ice_servers.length > 0) {
for (const server of infraConfig.ice_servers) {
@@ -389,27 +483,26 @@
}
let pc = createPeerConnection(infraConfig, control);
- let audioStream;
+ let mediaStream;
try {
- audioStream =
- await navigator.mediaDevices.getUserMedia({video: false, audio: true});
- const audioTracks = audioStream.getAudioTracks();
- if (audioTracks.length > 0) {
- console.log(`Using Audio device: ${audioTracks[0].label}, with ${
- audioTracks.length} tracks`);
- audioTracks.forEach(track => pc.addTrack(track, audioStream));
- }
+ mediaStream =
+ await navigator.mediaDevices.getUserMedia({audio: true});
+ const tracks = mediaStream.getTracks();
+ tracks.forEach(track => {
+ console.info(`Using ${track.kind} device: ${track.label}`);
+ pc.addTrack(track, mediaStream);
+ });
} catch (e) {
- console.error("Failed to open audio device: ", e);
+ console.error('Failed to open device: ', e);
}
- let deviceConnection = new DeviceConnection(pc, control, audioStream);
+ let deviceConnection = new DeviceConnection(pc, control, mediaStream);
deviceConnection.description = deviceInfo;
async function acceptOfferAndReplyAnswer(offer) {
try {
await pc.setRemoteDescription(offer);
let answer = await pc.createAnswer();
- console.log('Answer: ', answer);
+ console.debug('Answer: ', answer);
await pc.setLocalDescription(answer);
await control.sendClientDescription(answer);
} catch (e) {
@@ -418,11 +511,11 @@
}
}
control.onOffer(desc => {
- console.log('Offer: ', desc);
+ console.debug('Offer: ', desc);
acceptOfferAndReplyAnswer(desc);
});
control.onIceCandidate(iceCandidate => {
- console.log(`Remote ICE Candidate: `, iceCandidate);
+ console.debug(`Remote ICE Candidate: `, iceCandidate);
pc.addIceCandidate(iceCandidate);
});
diff --git a/host/frontend/webrtc_operator/assets/js/controls.js b/host/frontend/webrtc_operator/assets/js/controls.js
index 31db046..833cc15 100644
--- a/host/frontend/webrtc_operator/assets/js/controls.js
+++ b/host/frontend/webrtc_operator/assets/js/controls.js
@@ -15,24 +15,156 @@
*/
function createToggleControl(elm, iconName, onChangeCb) {
- let icon = document.createElement("span");
- icon.classList.add("toggle-control-icon");
- icon.classList.add("material-icons-outlined");
+ let icon = document.createElement('span');
+ icon.classList.add('toggle-control-icon');
+ icon.classList.add('material-icons-outlined');
if (iconName) {
icon.appendChild(document.createTextNode(iconName));
}
elm.appendChild(icon);
- let toggle = document.createElement("label");
- toggle.classList.add("toggle-control-switch");
- let input = document.createElement("input");
- input.type = "checkbox";
+ let toggle = document.createElement('label');
+ toggle.classList.add('toggle-control-switch');
+ let input = document.createElement('input');
+ input.type = 'checkbox';
toggle.appendChild(input);
- let slider = document.createElement("span");
- slider.classList.add("toggle-control-slider");
+ let slider = document.createElement('span');
+ slider.classList.add('toggle-control-slider');
toggle.appendChild(slider);
- elm.classList.add("toggle-control");
+ elm.classList.add('toggle-control');
elm.appendChild(toggle);
- if (onChangeCb) {
- input.onchange = e => onChangeCb(e.target.checked);
+ return {
+ // A callback can later be associated with the toggle element by calling
+ // .OnClick(onChangeCb) on the returned object. The callback should accept a
+ // boolean parameter indicating whether the toggle is in ON position.
+ OnClick: cb => input.onchange = e => cb(e.target.checked),
+ };
+}
+
+function createControlPanelButton(
+ command, title, icon_name, listener,
+ parent_id = 'control-panel-default-buttons') {
+ let button = document.createElement('button');
+ document.getElementById(parent_id).appendChild(button);
+ button.title = title;
+ button.dataset.command = command;
+ button.disabled = true;
+ // Capture mousedown/up/out commands instead of click to enable
+ // hold detection. mouseout is used to catch if the user moves the
+ // mouse outside the button while holding down.
+ button.addEventListener('mousedown', listener);
+ button.addEventListener('mouseup', listener);
+ button.addEventListener('mouseout', listener);
+ // Set the button image using Material Design icons.
+ // See http://google.github.io/material-design-icons
+ // and https://material.io/resources/icons
+ button.classList.add('material-icons');
+ button.innerHTML = icon_name;
+ return button;
+}
+
+function createModalButton(button_id, modal_id, close_id) {
+ const modalButton = document.getElementById(button_id);
+ const modalDiv = document.getElementById(modal_id);
+ const modalHeader = modalDiv.querySelector('.modal-header');
+ const modalClose = document.getElementById(close_id);
+
+ // Position the modal to the right of the show modal button.
+ modalDiv.style.top = modalButton.offsetTop;
+ modalDiv.style.left = modalButton.offsetWidth + 30;
+
+ function showHideModal(show) {
+ if (show) {
+ modalButton.classList.add('modal-button-opened')
+ modalDiv.style.display = 'block';
+ } else {
+ modalButton.classList.remove('modal-button-opened')
+ modalDiv.style.display = 'none';
+ }
}
+ // Allow the show modal button to toggle the modal,
+ modalButton.addEventListener(
+ 'click', evt => showHideModal(modalDiv.style.display != 'block'));
+ // but the close button always closes.
+ modalClose.addEventListener('click', evt => showHideModal(false));
+
+ // Allow the modal to be dragged by the header.
+ let modalOffsets = {
+ midDrag: false,
+ mouseDownOffsetX: null,
+ mouseDownOffsetY: null,
+ };
+ modalHeader.addEventListener('mousedown', evt => {
+ modalOffsets.midDrag = true;
+ // Store the offset of the mouse location from the
+ // modal's current location.
+ modalOffsets.mouseDownOffsetX = parseInt(modalDiv.style.left) - evt.clientX;
+ modalOffsets.mouseDownOffsetY = parseInt(modalDiv.style.top) - evt.clientY;
+ });
+ modalHeader.addEventListener('mousemove', evt => {
+ let offsets = modalOffsets;
+ if (offsets.midDrag) {
+ // Move the modal to the mouse location plus the
+ // offset calculated on the initial mouse-down.
+ modalDiv.style.left = evt.clientX + offsets.mouseDownOffsetX;
+ modalDiv.style.top = evt.clientY + offsets.mouseDownOffsetY;
+ }
+ });
+ document.addEventListener('mouseup', evt => {
+ modalOffsets.midDrag = false;
+ });
+}
+
+function cmdConsole(consoleViewName, consoleInputName) {
+ let consoleView = document.getElementById(consoleViewName);
+
+ let addString =
+ function(str) {
+ consoleView.value += str;
+ consoleView.scrollTop = consoleView.scrollHeight;
+ }
+
+ let addLine =
+ function(line) {
+ addString(line + '\r\n');
+ }
+
+ let commandCallbacks = [];
+
+ let addCommandListener =
+ function(f) {
+ commandCallbacks.push(f);
+ }
+
+ let onCommand =
+ function(cmd) {
+ cmd = cmd.trim();
+
+ if (cmd.length == 0) return;
+
+ commandCallbacks.forEach(f => {
+ f(cmd);
+ })
+ }
+
+ addCommandListener(cmd => addLine('>> ' + cmd));
+
+ let consoleInput = document.getElementById(consoleInputName);
+
+ consoleInput.addEventListener('keydown', e => {
+ if ((e.key && e.key == 'Enter') || e.keyCode == 13) {
+ let command = e.target.value;
+
+ e.target.value = '';
+
+ onCommand(command);
+ }
+ });
+
+ return {
+ consoleView: consoleView,
+ consoleInput: consoleInput,
+ addLine: addLine,
+ addString: addString,
+ addCommandListener: addCommandListener,
+ };
}
diff --git a/host/frontend/webrtc_operator/assets/js/rootcanal.js b/host/frontend/webrtc_operator/assets/js/rootcanal.js
new file mode 100644
index 0000000..e3783c7
--- /dev/null
+++ b/host/frontend/webrtc_operator/assets/js/rootcanal.js
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 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 strict';
+
+function rootCanalCalculateMessageSize(name, args) {
+ let result = 0;
+
+ result += 1 + name.length; // length of name + it's data
+ result += 1; // count of args
+
+ for (let i = 0; i < args.length; i++) {
+ result += 1; // length of args[i]
+ result += args[i].length; // data of args[i]
+ }
+
+ return result;
+}
+
+function rootCanalAddU8(array, pos, val) {
+ array[pos] = val & 0xff;
+
+ return pos + 1;
+}
+
+function rootCanalAddPayload(array, pos, payload) {
+ array.set(payload, pos);
+
+ return pos + payload.length;
+}
+
+function rootCanalAddString(array, pos, val) {
+ let curPos = pos;
+
+ curPos = rootCanalAddU8(array, curPos, val.length);
+
+ return rootCanalAddPayload(array, curPos, utf8Encoder.encode(val));
+}
+
+function createRootcanalMessage(command, args) {
+ let messageSize = rootCanalCalculateMessageSize(command, args);
+ let arrayBuffer = new ArrayBuffer(messageSize);
+ let array = new Uint8Array(arrayBuffer);
+ let pos = 0;
+
+ pos = rootCanalAddString(array, pos, command);
+ pos = rootCanalAddU8(array, pos, args.length);
+
+ for (let i = 0; i < args.length; i++) {
+ pos = rootCanalAddString(array, pos, args[i]);
+ }
+
+ return array;
+}
+
+function decodeRootcanalMessage(array) {
+ let size = array[0];
+ let message = array.slice(1);
+
+ return utf8Decoder.decode(message);
+}
diff --git a/host/frontend/webrtc_operator/assets/style.css b/host/frontend/webrtc_operator/assets/style.css
index ca4e7b2..61a19b1 100644
--- a/host/frontend/webrtc_operator/assets/style.css
+++ b/host/frontend/webrtc_operator/assets/style.css
@@ -40,12 +40,19 @@
}
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.spinner {
+ animation: spin 1.4s linear infinite;
+}
+
/* Top header row. */
#header {
height: 64px;
- margin-top: 10px;
- margin-bottom: 10px;
/* Items inside this use a row Flexbox.*/
display: flex;
align-items: center;
@@ -60,9 +67,26 @@
margin-right: 6px;
}
#device-audio {
- margin-bottom: 5px;
+ height: 44px;
}
+#error-message-div {
+ flex-grow: 1;
+}
+#error-message {
+ color: white;
+ font-family: 'Open Sans', sans-serif;
+ padding: 10px;
+ margin: 10px;
+}
+#error-message.warning {
+ /* dark red */
+ background-color: #927836;
+}
+#error-message.error {
+ /* dark red */
+ background-color: #900000;
+}
#status-div {
flex-grow: 1;
}
@@ -87,23 +111,24 @@
/* Control panel buttons and device screen(s). */
-#controls-and-screens {
+#controls-and-displays {
height: calc(100% - 84px);
/* Items inside this use a row Flexbox.*/
display: flex;
}
-#controls-and-screens div {
- margin-left: 10px;
- margin-right: 10px;
+
+#controls-and-displays > div {
+ margin-left: 5px;
+ margin-right: 5px;
}
-#device-details-modal {
+.modal {
/* Start out hidden, and use absolute positioning. */
display: none;
position: absolute;
- border-radius: 16px;
+ border-radius: 10px;
padding: 20px;
padding-top: 1px;
@@ -111,12 +136,13 @@
color: white;
font-family: 'Open Sans', sans-serif;
}
-#device-details-modal-header {
+.modal-header {
+ cursor: move;
/* Items inside this use a row Flexbox.*/
display: flex;
justify-content: space-between;
}
-#device-details-close {
+.modal-close {
color: white;
border: none;
outline: none;
@@ -125,9 +151,15 @@
#device-details-modal span {
white-space: pre;
}
+#bluetooth-console-input {
+ width: 100%;
+}
+#bluetooth-console-cmd-label {
+ color: white;
+}
.control-panel-column {
- width: 80px;
+ width: 50px;
/* Items inside this use a column Flexbox.*/
display: flex;
flex-direction: column;
@@ -137,13 +169,13 @@
/* Give the custom buttons column a blue background. */
background-color: #1c4587ff;
height: fit-content;
- border-radius: 16px;
+ border-radius: 10px;
}
.control-panel-column button {
- margin: 10px;
- height: 60px;
- font-size: 48px;
+ margin: 0px 0px 5px 0px;
+ height: 50px;
+ font-size: 32px;
color: #e8eaed; /* Google grey 200 */
border: none;
@@ -153,22 +185,34 @@
.control-panel-column button:disabled {
color: #9aa0a6; /* Google grey 500 */
}
-#device-details-button {
- margin: 0px;
- height: 80px;
- border-radius: 16px;
+.control-panel-column button.modal-button-opened {
+ border-radius: 10px;
background-color: #5f6368; /* Google grey 700 */
}
-#screens {
+#device-displays {
/* Take up the remaining width of the window.*/
flex-grow: 1;
/* Don't grow taller than the window.*/
max-height: 100vh;
+ /* Allows child elements to be positioned relative to this element. */
+ position: relative;
}
-#device-screen {
+/*
+ * Container <div> used to wrap each display's <video> element which is used for
+ * maintaining each display's width and height while the display is potentially
+ * rotating.
+ */
+.device-display {
+ /* Prevents #device-displays from using this element when computing flex size. */
+ position: absolute;
+}
+
+/* The actual <video> element for each display. */
+.device-display-video {
+ position: absolute;
+ left: 0px;
touch-action: none;
- transform-origin: top right;
object-fit: cover;
}
diff --git a/host/frontend/webrtc_operator/certs/create_certs.sh b/host/frontend/webrtc_operator/certs/create_certs.sh
index 9f85e40..fefc275 100755
--- a/host/frontend/webrtc_operator/certs/create_certs.sh
+++ b/host/frontend/webrtc_operator/certs/create_certs.sh
@@ -3,15 +3,15 @@
# As explained in
# https://gist.github.com/darrenjs/4645f115d10aa4b5cebf57483ec82eca
-openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
-openssl rsa -passin pass:x -in server.pass.key -out server.key
+openssl genrsa -des3 -passout pass:xxxx -out server.pass.key 2048
+openssl rsa -passin pass:xxxx -in server.pass.key -out server.key
rm -f server.pass.key
openssl req \
-subj "/C=US/ST=California/L=Santa Clara/O=Beyond Aggravated/CN=localhost" \
-new -key server.key -out server.csr
-openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
+openssl x509 -req -sha256 -days 99999 -in server.csr -signkey server.key -out server.crt
rm -f server.csr
# Now create the list of certificates we trust as a client.
diff --git a/host/frontend/webrtc_operator/certs/server.crt b/host/frontend/webrtc_operator/certs/server.crt
index 0b9aa36..f871fcb 100644
--- a/host/frontend/webrtc_operator/certs/server.crt
+++ b/host/frontend/webrtc_operator/certs/server.crt
@@ -1,20 +1,20 @@
-----BEGIN CERTIFICATE-----
-MIIDVzCCAj8CFBXBydw0e/7l31d9fzO7vrBxAw4yMA0GCSqGSIb3DQEBCwUAMGgx
+MIIDWTCCAkECFFsSgyQAZHRltRAdi9SrK2ZHRa1/MA0GCSqGSIb3DQEBCwUAMGgx
CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQHDAtTYW50
YSBDbGFyYTEaMBgGA1UECgwRQmV5b25kIEFnZ3JhdmF0ZWQxEjAQBgNVBAMMCWxv
-Y2FsaG9zdDAeFw0yMDA1MDcyMTMzMDFaFw0yMTA1MDcyMTMzMDFaMGgxCzAJBgNV
-BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQHDAtTYW50YSBDbGFy
-YTEaMBgGA1UECgwRQmV5b25kIEFnZ3JhdmF0ZWQxEjAQBgNVBAMMCWxvY2FsaG9z
-dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPdz0Tom1NSujwxYFhG2
-MnqTTU5F9E5OwnO9svlXchXozJSoYpuFG43ZI/9exVmhQKZ4WwJUX74beYuZh611
-S1v9nAiAX+w3lpaiH/9gNH9PaR6kyOTveS9DtHqHlsHm9Ahuls/6mIlHVLsfGVcS
-DDIu5eYqBU0Xq1RYm3+9EUtEOLPQGfcaSUTnI6AkZ55TcJiKhq0CIoTpv/I+7mlw
-zsqPi2f2G7kI47bz1aiXeh34jelKR321fKl1/DW3F0CLSj0/u4gMgNIgPB/tHIKj
-GiNnvJTE7ZDSV34oUmqKhKkUixwjFHUFpMislpIJTsefzaKE4NLa57g5qgAnaofw
-m1UCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEASZx0QGNR5DT8vUgEBTMD1OKG3rFw
-zXLI1Lsn5nIMSGkL7aIlx7D8lbdvy0OS+Cg8jE256yiM7cZTF07rKwUeI2v/wDrX
-KP9qfMhonICrbQyKlZ6J4hLVV9wCkYQnMqwS+uSH1l1X+qr3ZCcamgTZ2hrhJFy4
-HEeoC4qdL0+uM2NhrjmPBvqMq9hYWe3nAREmRjSAxBMawjThldLqQCooyvtMskkn
-QAzPte/qvP4kWRpI+KQEv9Rc8iI9PNCF9+W4zl6pIyRDRVYWx3C1PSdniaTc/yDQ
-FL5UbuZ5ujUOdvMy1yAlcTiDVo+Ke7ybAK9FhEBxMPELyTFTY0GVKI46QA==
+Y2FsaG9zdDAgFw0yMTA1MDgwNjUwMDBaGA8yMjk1MDIyMDA2NTAwMFowaDELMAkG
+A1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFDASBgNVBAcMC1NhbnRhIENs
+YXJhMRowGAYDVQQKDBFCZXlvbmQgQWdncmF2YXRlZDESMBAGA1UEAwwJbG9jYWxo
+b3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxijHKeNl7nlYwn3f
+6CG8MP3XDVI3bc4v+XIJElH2mrNz8AUd0I9FoJVEdCDnRIO1Vb7sN/Wn9fxMQQv2
+ReHE56kR36Ca19G5VFo789gt7268ibpV7QQ117aoeEdw7kpOukKUG87Q7bZWlsZ3
+FX/nxSv1Hnv5BABxo0uyM8tv5KGXWwR8bsmFCCEr8i2AtglOnyVSV3Ey18Vb/mgt
++E4YE6WobTAiP8UdEKTPdnBms9IcHNknTB+ux910xDH4z9fWv8+4Bp4mH43xvbrt
+GJEzDl1xQ+Mrzc7Hk0FJqUlT6WHuyM9Tk7jspmhXxggtecy0CGB/hSo//urrUu2L
+EUABgQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKNKU5S6HmO6atYLFmBfBk2ms/
+ZkDNHtDxnKLmsX3CFthRI3mGz1oEaMzYb2G2ufDxzh4/x2DM4iffRgj2knHSGlun
+3NYClZLM18/KTpXF26bycnixVGVw26jG0WHQTSdCS5VLz8CSg1O/487aEC5sKtoe
+LSk4KMpDFzD0+Esbkf/1aw0VyxsxjHRnxIMheK4cUt3y5I5qIKS2qK0W9ZuWr1Er
+dkYKCVumZKxVuMhHVOck6zLC5lZjefkFtTtyLe2nKIvXNWcHyuKqmLfafhEVSlKP
+Idg90Qo9+TY4bwQ8sMRVcVlnnChGvDtmeBXz4xwyFg8Pzhvwa86PlwMISi/m
-----END CERTIFICATE-----
diff --git a/host/frontend/webrtc_operator/certs/server.key b/host/frontend/webrtc_operator/certs/server.key
index 616a4ce..a6b8f9f 100644
--- a/host/frontend/webrtc_operator/certs/server.key
+++ b/host/frontend/webrtc_operator/certs/server.key
@@ -1,27 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
-MIIEpgIBAAKCAQEA93PROibU1K6PDFgWEbYyepNNTkX0Tk7Cc72y+VdyFejMlKhi
-m4Ubjdkj/17FWaFApnhbAlRfvht5i5mHrXVLW/2cCIBf7DeWlqIf/2A0f09pHqTI
-5O95L0O0eoeWweb0CG6Wz/qYiUdUux8ZVxIMMi7l5ioFTRerVFibf70RS0Q4s9AZ
-9xpJROcjoCRnnlNwmIqGrQIihOm/8j7uaXDOyo+LZ/YbuQjjtvPVqJd6HfiN6UpH
-fbV8qXX8NbcXQItKPT+7iAyA0iA8H+0cgqMaI2e8lMTtkNJXfihSaoqEqRSLHCMU
-dQWkyKyWkglOx5/NooTg0trnuDmqACdqh/CbVQIDAQABAoIBAQCnBGrxvwfjzTYL
-9OBgcAM+LHH/JMQynoIssJs+JEGCfDCpHcYAhiUE5syfLo4xYt9J/O4gcmZ04AJ3
-sNacwxBsNI6+Rjd4LkTbwu2p5ntIeobPAhX+P4wh1KbaFO4yTfnkPxBXrCKMdbLA
-4cqutCW7MWBGq5IMaK9hLLU30Jr9muiEIQ8tnexghJ4SiHnpju471KD9N77dRVZp
-TmUnIlJLqYMNoPj0nLwy6p1isq1KSg56j27OooL8piqOp2cy82GqjNeCfFcW74dJ
-lYmxILLseJiJvM2nlfFdSOvSOtaBkSG5oKsOO1K56u3cXhwSXChu5b8ewNjG5d+Y
-KY5obpV5AoGBAP/nJGxv7ljsg/pAqfWOwR5g6Iz9JxpgweubVt9fvnS+2Pj4akrJ
-1NZZCB/9zFbYf2Of2VMBdrNw7eS2+QDwj2TYPBjIH3WMPSByyyQaNna47b26Sxu2
-kCMhkoEMvwXEEQrLZ7I/sHgzYIEGg/9yLUJmzfdqpACU4bA1zkzKQ6tnAoGBAPeL
-2qn7ch3vlVLK46tm99Jkbw2SxoJajlZ4p2kpe/EexyqInfZJ58IKQNonqYNWMOwg
-aLeoyf+XsWgnuS4njMBPLmGTa/Pibs2b/0jrHP3mp2LVrRaL+wOUDM/gzmt7NeD+
-zZ8fD4Lsj8lXLfys5M5HrosF8c3TT8odFALjLwnjAoGBAKmH3qh8CsIchl6O4knM
-tgHDH6zvtS0TdsT4lzfKfSlomeNu5zP+vCL4vpo7EFlkehhs+JO1/4ZnRSLlWNcX
-h1e+rSmZwsWkD4bkpdGYEAbdAptTxJhqfNjZT+5wnEhcmRG2qU78RJONLdysjVv4
-ryUzaDYGDvpXp6CONMrIoMX3AoGBAJV/bbg4dauklD6i7yoFjmcOZo8A9EenHs0U
-Iq588jAlUUzbouIpsgBapt3ZFCOQOw1vaS55jjyAxRBM5SX9lqBRcYZWPNzWA+rC
-akMEUsb3tGEZAGZcdWSs1av5bVA14c0WtOGDJaAA87k5oDk3xRra6YtmNKkEE+zQ
-8NPple/XAoGBAJMpXdSP+Hgakv//HaQaBcenHk1f8v4b8t48NnXjou3ojfO8Wzgr
-odwlninHk1PaQD0XnFIISMVD9d2aSX8X1YQVHUQ9IQiA4hLy2lxZbP2K+LEaVKDm
-r7cjRKixJTIGm6vZK8pr1l5XD3657fQte4YLei5XA+i/UgFZyIA0KsKo
+MIIEpQIBAAKCAQEAxijHKeNl7nlYwn3f6CG8MP3XDVI3bc4v+XIJElH2mrNz8AUd
+0I9FoJVEdCDnRIO1Vb7sN/Wn9fxMQQv2ReHE56kR36Ca19G5VFo789gt7268ibpV
+7QQ117aoeEdw7kpOukKUG87Q7bZWlsZ3FX/nxSv1Hnv5BABxo0uyM8tv5KGXWwR8
+bsmFCCEr8i2AtglOnyVSV3Ey18Vb/mgt+E4YE6WobTAiP8UdEKTPdnBms9IcHNkn
+TB+ux910xDH4z9fWv8+4Bp4mH43xvbrtGJEzDl1xQ+Mrzc7Hk0FJqUlT6WHuyM9T
+k7jspmhXxggtecy0CGB/hSo//urrUu2LEUABgQIDAQABAoIBAQCt5QsiT1QcOpER
+3LSpWTF1LM2T+xp5Wf/vv4sGcLcge2q6r0LCy3gmu9ceseFB1vNDFBDn6sRCse2Z
+B45PNRk+0rfEr4Qy8PDafXUvP/7PpzX9B3BwVsmJS9n783W/J6Z+/f5LiOsAMIs8
+NV47l8sk1LZ+0fxs7pbK3pq7qUPANiEgKQ2F6PBkJkORuHmfhccC/a+qhAdsqwVB
+DjjwY0e5A/4fWqLiIUJGBopv0+df1TWsqfTq4lwaSDPY6dyI9E3MYVxKiYpbqgQ/
+N7QtjDjut6Zuw10bCgYyuEgy3Ab5Pmx6Hs6VGuYI6Hge4JeZ6TzZJ/cNnONZvGTF
+3356FYABAoGBAPgA+JYHDgXl4W6n9uMYQ4ZXL1UrYVxe8B1wWgt7DKD98/fD+Jj1
+KuRRUB2wLv/Jis+48QlAwLHQIL35WGpGm9C9j9dS3NO199BLrDY4fteSr67NrG9P
+Q0H+3F/7Dx7Scg5LNRYe6ZNvdfUD+9zoiaNHa9MMriK0ffc4O08RkA3BAoGBAMyM
+Y6c83Wek6GzPu5GCSMxEGBdqbx8T3iyEo4J23N3WAcfdIgSWZcjB2wNdNqa/RuHW
+QH6Gns5DLO3pYLU7R9DgLK5VE/Nq4nF0o7D57DnRkT+sZ3gYPdC7LiG3d5c+J7k1
+3pKL8yh7t2AgMpopfaY70wV4gL1K2qOLYfxbVGPBAoGBAK/Y2GpghDPwZOD2Xdt2
+R+LIjPpB8R3y/ySQlnhPfovkpYlHvkyOgiQz96+lTh32ROO2ycn6zOcHoT+yvltU
+x4TB9G0EBypie12JWolzk5S9IK68jQi71f/Ee3Pe60C6jT7PWsvdjVcKEERz17Ey
+fO12ZeDWu95Fxo91orAUzuTBAoGAC2xbtF9Fzh/7ivge9YVdI2s6HTSoeAfYBIxz
+xTl2JD1rZAoJeFAd5xRMcuelwbI09y/L8kT6YXKG89JwwC5LWHLsi9/ceV+ivctR
+yPRsKN53SiMKtD5GVX3ematxVlT2SvWjNHP0ZHJkT039BXcDuWDl7AxKxEeF5lRG
+aJ2BHQECgYEA0y67wxrbrgLGnur1J3nMN/sXmaTp6aDg/fTJyaaYhAzt+EUIn3MC
+nqVrWC35lpD5TO1fo8kfyaQH94zQxkywVb018caXHotjHC+EN6VnrG8cXmEvXU8E
+rUmEzHAxy548ZAgV6I/2kIrDEzijElvq7Geq2MzBkOUnFpD2y030iQ0=
-----END RSA PRIVATE KEY-----
diff --git a/host/frontend/webrtc_operator/certs/server.p12 b/host/frontend/webrtc_operator/certs/server.p12
index 87a94c5..80920ba 100644
--- a/host/frontend/webrtc_operator/certs/server.p12
+++ b/host/frontend/webrtc_operator/certs/server.p12
Binary files differ
diff --git a/host/frontend/webrtc_operator/certs/trusted.pem b/host/frontend/webrtc_operator/certs/trusted.pem
index 8097b16..0c50578 100644
--- a/host/frontend/webrtc_operator/certs/trusted.pem
+++ b/host/frontend/webrtc_operator/certs/trusted.pem
@@ -2,69 +2,69 @@
Data:
Version: 1 (0x0)
Serial Number:
- 15:c1:c9:dc:34:7b:fe:e5:df:57:7d:7f:33:bb:be:b0:71:03:0e:32
+ 5b:12:83:24:00:64:74:65:b5:10:1d:8b:d4:ab:2b:66:47:45:ad:7f
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, ST = California, L = Santa Clara, O = Beyond Aggravated, CN = localhost
Validity
- Not Before: May 7 21:33:01 2020 GMT
- Not After : May 7 21:33:01 2021 GMT
+ Not Before: May 8 06:50:00 2021 GMT
+ Not After : Feb 20 06:50:00 2295 GMT
Subject: C = US, ST = California, L = Santa Clara, O = Beyond Aggravated, CN = localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
- 00:f7:73:d1:3a:26:d4:d4:ae:8f:0c:58:16:11:b6:
- 32:7a:93:4d:4e:45:f4:4e:4e:c2:73:bd:b2:f9:57:
- 72:15:e8:cc:94:a8:62:9b:85:1b:8d:d9:23:ff:5e:
- c5:59:a1:40:a6:78:5b:02:54:5f:be:1b:79:8b:99:
- 87:ad:75:4b:5b:fd:9c:08:80:5f:ec:37:96:96:a2:
- 1f:ff:60:34:7f:4f:69:1e:a4:c8:e4:ef:79:2f:43:
- b4:7a:87:96:c1:e6:f4:08:6e:96:cf:fa:98:89:47:
- 54:bb:1f:19:57:12:0c:32:2e:e5:e6:2a:05:4d:17:
- ab:54:58:9b:7f:bd:11:4b:44:38:b3:d0:19:f7:1a:
- 49:44:e7:23:a0:24:67:9e:53:70:98:8a:86:ad:02:
- 22:84:e9:bf:f2:3e:ee:69:70:ce:ca:8f:8b:67:f6:
- 1b:b9:08:e3:b6:f3:d5:a8:97:7a:1d:f8:8d:e9:4a:
- 47:7d:b5:7c:a9:75:fc:35:b7:17:40:8b:4a:3d:3f:
- bb:88:0c:80:d2:20:3c:1f:ed:1c:82:a3:1a:23:67:
- bc:94:c4:ed:90:d2:57:7e:28:52:6a:8a:84:a9:14:
- 8b:1c:23:14:75:05:a4:c8:ac:96:92:09:4e:c7:9f:
- cd:a2:84:e0:d2:da:e7:b8:39:aa:00:27:6a:87:f0:
- 9b:55
+ 00:c6:28:c7:29:e3:65:ee:79:58:c2:7d:df:e8:21:
+ bc:30:fd:d7:0d:52:37:6d:ce:2f:f9:72:09:12:51:
+ f6:9a:b3:73:f0:05:1d:d0:8f:45:a0:95:44:74:20:
+ e7:44:83:b5:55:be:ec:37:f5:a7:f5:fc:4c:41:0b:
+ f6:45:e1:c4:e7:a9:11:df:a0:9a:d7:d1:b9:54:5a:
+ 3b:f3:d8:2d:ef:6e:bc:89:ba:55:ed:04:35:d7:b6:
+ a8:78:47:70:ee:4a:4e:ba:42:94:1b:ce:d0:ed:b6:
+ 56:96:c6:77:15:7f:e7:c5:2b:f5:1e:7b:f9:04:00:
+ 71:a3:4b:b2:33:cb:6f:e4:a1:97:5b:04:7c:6e:c9:
+ 85:08:21:2b:f2:2d:80:b6:09:4e:9f:25:52:57:71:
+ 32:d7:c5:5b:fe:68:2d:f8:4e:18:13:a5:a8:6d:30:
+ 22:3f:c5:1d:10:a4:cf:76:70:66:b3:d2:1c:1c:d9:
+ 27:4c:1f:ae:c7:dd:74:c4:31:f8:cf:d7:d6:bf:cf:
+ b8:06:9e:26:1f:8d:f1:bd:ba:ed:18:91:33:0e:5d:
+ 71:43:e3:2b:cd:ce:c7:93:41:49:a9:49:53:e9:61:
+ ee:c8:cf:53:93:b8:ec:a6:68:57:c6:08:2d:79:cc:
+ b4:08:60:7f:85:2a:3f:fe:ea:eb:52:ed:8b:11:40:
+ 01:81
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
- 49:9c:74:40:63:51:e4:34:fc:bd:48:04:05:33:03:d4:e2:86:
- de:b1:70:cd:72:c8:d4:bb:27:e6:72:0c:48:69:0b:ed:a2:25:
- c7:b0:fc:95:b7:6f:cb:43:92:f8:28:3c:8c:4d:b9:eb:28:8c:
- ed:c6:53:17:4e:eb:2b:05:1e:23:6b:ff:c0:3a:d7:28:ff:6a:
- 7c:c8:68:9c:80:ab:6d:0c:8a:95:9e:89:e2:12:d5:57:dc:02:
- 91:84:27:32:ac:12:fa:e4:87:d6:5d:57:fa:aa:f7:64:27:1a:
- 9a:04:d9:da:1a:e1:24:5c:b8:1c:47:a8:0b:8a:9d:2f:4f:ae:
- 33:63:61:ae:39:8f:06:fa:8c:ab:d8:58:59:ed:e7:01:11:26:
- 46:34:80:c4:13:1a:c2:34:e1:95:d2:ea:40:2a:28:ca:fb:4c:
- b2:49:27:40:0c:cf:b5:ef:ea:bc:fe:24:59:1a:48:f8:a4:04:
- bf:d4:5c:f2:22:3d:3c:d0:85:f7:e5:b8:ce:5e:a9:23:24:43:
- 45:56:16:c7:70:b5:3d:27:67:89:a4:dc:ff:20:d0:14:be:54:
- 6e:e6:79:ba:35:0e:76:f3:32:d7:20:25:71:38:83:56:8f:8a:
- 7b:bc:9b:00:af:45:84:40:71:30:f1:0b:c9:31:53:63:41:95:
- 28:8e:3a:40
+ 4a:34:a5:39:4b:a1:e6:3b:a6:ad:60:b1:66:05:f0:64:da:6b:
+ 3f:66:40:cd:1e:d0:f1:9c:a2:e6:b1:7d:c2:16:d8:51:23:79:
+ 86:cf:5a:04:68:cc:d8:6f:61:b6:b9:f0:f1:ce:1e:3f:c7:60:
+ cc:e2:27:df:46:08:f6:92:71:d2:1a:5b:a7:dc:d6:02:95:92:
+ cc:d7:cf:ca:4e:95:c5:db:a6:f2:72:78:b1:54:65:70:db:a8:
+ c6:d1:61:d0:4d:27:42:4b:95:4b:cf:c0:92:83:53:bf:e3:ce:
+ da:10:2e:6c:2a:da:1e:2d:29:38:28:ca:43:17:30:f4:f8:4b:
+ 1b:91:ff:f5:6b:0d:15:cb:1b:31:8c:74:67:c4:83:21:78:ae:
+ 1c:52:dd:f2:e4:8e:6a:20:a4:b6:a8:ad:16:f5:9b:96:af:51:
+ 2b:76:46:0a:09:5b:a6:64:ac:55:b8:c8:47:54:e7:24:eb:32:
+ c2:e6:56:63:79:f9:05:b5:3b:72:2d:ed:a7:28:8b:d7:35:67:
+ 07:ca:e2:aa:98:b7:da:7e:11:15:4a:52:8f:21:d8:3d:d1:0a:
+ 3d:f9:36:38:6f:04:3c:b0:c4:55:71:59:67:9c:28:46:bc:3b:
+ 66:78:15:f3:e3:1c:32:16:0f:0f:ce:1b:f0:6b:ce:8f:97:03:
+ 08:4a:2f:e6
-----BEGIN CERTIFICATE-----
-MIIDVzCCAj8CFBXBydw0e/7l31d9fzO7vrBxAw4yMA0GCSqGSIb3DQEBCwUAMGgx
+MIIDWTCCAkECFFsSgyQAZHRltRAdi9SrK2ZHRa1/MA0GCSqGSIb3DQEBCwUAMGgx
CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQHDAtTYW50
YSBDbGFyYTEaMBgGA1UECgwRQmV5b25kIEFnZ3JhdmF0ZWQxEjAQBgNVBAMMCWxv
-Y2FsaG9zdDAeFw0yMDA1MDcyMTMzMDFaFw0yMTA1MDcyMTMzMDFaMGgxCzAJBgNV
-BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQHDAtTYW50YSBDbGFy
-YTEaMBgGA1UECgwRQmV5b25kIEFnZ3JhdmF0ZWQxEjAQBgNVBAMMCWxvY2FsaG9z
-dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPdz0Tom1NSujwxYFhG2
-MnqTTU5F9E5OwnO9svlXchXozJSoYpuFG43ZI/9exVmhQKZ4WwJUX74beYuZh611
-S1v9nAiAX+w3lpaiH/9gNH9PaR6kyOTveS9DtHqHlsHm9Ahuls/6mIlHVLsfGVcS
-DDIu5eYqBU0Xq1RYm3+9EUtEOLPQGfcaSUTnI6AkZ55TcJiKhq0CIoTpv/I+7mlw
-zsqPi2f2G7kI47bz1aiXeh34jelKR321fKl1/DW3F0CLSj0/u4gMgNIgPB/tHIKj
-GiNnvJTE7ZDSV34oUmqKhKkUixwjFHUFpMislpIJTsefzaKE4NLa57g5qgAnaofw
-m1UCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEASZx0QGNR5DT8vUgEBTMD1OKG3rFw
-zXLI1Lsn5nIMSGkL7aIlx7D8lbdvy0OS+Cg8jE256yiM7cZTF07rKwUeI2v/wDrX
-KP9qfMhonICrbQyKlZ6J4hLVV9wCkYQnMqwS+uSH1l1X+qr3ZCcamgTZ2hrhJFy4
-HEeoC4qdL0+uM2NhrjmPBvqMq9hYWe3nAREmRjSAxBMawjThldLqQCooyvtMskkn
-QAzPte/qvP4kWRpI+KQEv9Rc8iI9PNCF9+W4zl6pIyRDRVYWx3C1PSdniaTc/yDQ
-FL5UbuZ5ujUOdvMy1yAlcTiDVo+Ke7ybAK9FhEBxMPELyTFTY0GVKI46QA==
+Y2FsaG9zdDAgFw0yMTA1MDgwNjUwMDBaGA8yMjk1MDIyMDA2NTAwMFowaDELMAkG
+A1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFDASBgNVBAcMC1NhbnRhIENs
+YXJhMRowGAYDVQQKDBFCZXlvbmQgQWdncmF2YXRlZDESMBAGA1UEAwwJbG9jYWxo
+b3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxijHKeNl7nlYwn3f
+6CG8MP3XDVI3bc4v+XIJElH2mrNz8AUd0I9FoJVEdCDnRIO1Vb7sN/Wn9fxMQQv2
+ReHE56kR36Ca19G5VFo789gt7268ibpV7QQ117aoeEdw7kpOukKUG87Q7bZWlsZ3
+FX/nxSv1Hnv5BABxo0uyM8tv5KGXWwR8bsmFCCEr8i2AtglOnyVSV3Ey18Vb/mgt
++E4YE6WobTAiP8UdEKTPdnBms9IcHNknTB+ux910xDH4z9fWv8+4Bp4mH43xvbrt
+GJEzDl1xQ+Mrzc7Hk0FJqUlT6WHuyM9Tk7jspmhXxggtecy0CGB/hSo//urrUu2L
+EUABgQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKNKU5S6HmO6atYLFmBfBk2ms/
+ZkDNHtDxnKLmsX3CFthRI3mGz1oEaMzYb2G2ufDxzh4/x2DM4iffRgj2knHSGlun
+3NYClZLM18/KTpXF26bycnixVGVw26jG0WHQTSdCS5VLz8CSg1O/487aEC5sKtoe
+LSk4KMpDFzD0+Esbkf/1aw0VyxsxjHRnxIMheK4cUt3y5I5qIKS2qK0W9ZuWr1Er
+dkYKCVumZKxVuMhHVOck6zLC5lZjefkFtTtyLe2nKIvXNWcHyuKqmLfafhEVSlKP
+Idg90Qo9+TY4bwQ8sMRVcVlnnChGvDtmeBXz4xwyFg8Pzhvwa86PlwMISi/m
-----END CERTIFICATE-----
diff --git a/host/frontend/webrtc_operator/server.cpp b/host/frontend/webrtc_operator/server.cpp
index 565676a..bbc862c 100644
--- a/host/frontend/webrtc_operator/server.cpp
+++ b/host/frontend/webrtc_operator/server.cpp
@@ -31,7 +31,9 @@
DEFINE_bool(use_secure_http, true, "Whether to use HTTPS or HTTP.");
DEFINE_string(assets_dir, "webrtc",
"Directory with location of webpage assets.");
-DEFINE_string(certs_dir, "webrtc/certs", "Directory to certificates.");
+DEFINE_string(certs_dir, "webrtc/certs",
+ "Directory to certificates. It must contain a server.crt file, a "
+ "server.key file and (optionally) a CA.crt file.");
DEFINE_string(stun_server, "stun.l.google.com:19302",
"host:port of STUN server to use for public address resolution");
@@ -50,8 +52,13 @@
cuttlefish::DeviceRegistry device_registry;
cuttlefish::ServerConfig server_config({FLAGS_stun_server});
- cuttlefish::WebSocketServer wss(
- "webrtc-operator", FLAGS_certs_dir, FLAGS_assets_dir, FLAGS_http_server_port);
+ cuttlefish::WebSocketServer wss =
+ FLAGS_use_secure_http
+ ? cuttlefish::WebSocketServer("webrtc-operator", FLAGS_certs_dir,
+ FLAGS_assets_dir,
+ FLAGS_http_server_port)
+ : cuttlefish::WebSocketServer("webrtc-operator", FLAGS_assets_dir,
+ FLAGS_http_server_port);
auto device_handler_factory_p =
std::unique_ptr<cuttlefish::WebSocketHandlerFactory>(
diff --git a/host/frontend/webrtc_operator/signal_handler.cpp b/host/frontend/webrtc_operator/signal_handler.cpp
index d1d8bf3..92ceb26 100644
--- a/host/frontend/webrtc_operator/signal_handler.cpp
+++ b/host/frontend/webrtc_operator/signal_handler.cpp
@@ -60,6 +60,24 @@
handleMessage(type, json_message);
}
+void SignalHandler::OnReceive(const uint8_t* msg, size_t len, bool binary,
+ bool is_final) {
+ if (is_final) {
+ if (receive_buffer_.empty()) {
+ // no previous data - receive as-is
+ OnReceive(msg, len, binary);
+ } else {
+ // concatenate to previous data and receive
+ receive_buffer_.insert(receive_buffer_.end(), msg, msg + len);
+ OnReceive(receive_buffer_.data(), receive_buffer_.size(), binary);
+ receive_buffer_.clear();
+ }
+ } else {
+ // buffer up incomplete messages
+ receive_buffer_.insert(receive_buffer_.end(), msg, msg + len);
+ }
+}
+
void SignalHandler::SendServerConfig() {
// Call every time to allow config changes?
auto reply = server_config_.ToJson();
diff --git a/host/frontend/webrtc_operator/signal_handler.h b/host/frontend/webrtc_operator/signal_handler.h
index c278517..a0e813c 100644
--- a/host/frontend/webrtc_operator/signal_handler.h
+++ b/host/frontend/webrtc_operator/signal_handler.h
@@ -29,6 +29,8 @@
class SignalHandler : public WebSocketHandler {
public:
void OnReceive(const uint8_t* msg, size_t len, bool binary) override;
+ void OnReceive(const uint8_t* msg, size_t len, bool binary,
+ bool is_final) override;
void OnConnected() override;
protected:
SignalHandler(struct lws* wsi, DeviceRegistry* registry,
@@ -43,5 +45,6 @@
DeviceRegistry* registry_;
const ServerConfig& server_config_;
+ std::vector<uint8_t> receive_buffer_;
};
} // namespace cuttlefish
diff --git a/host/libs/audio_connector/commands.cpp b/host/libs/audio_connector/commands.cpp
index 42536d5..b77c726 100644
--- a/host/libs/audio_connector/commands.cpp
+++ b/host/libs/audio_connector/commands.cpp
@@ -15,6 +15,8 @@
#include "host/libs/audio_connector/commands.h"
+#include <algorithm>
+
#include <android-base/logging.h>
#include "host/libs/audio_connector/shm_layout.h"
@@ -27,6 +29,50 @@
<< " went out of scope without reply";
}
+JackInfoCommand::JackInfoCommand(uint32_t start_id, size_t count,
+ virtio_snd_jack_info* jack_info)
+ : InfoCommand(AudioCommandType::VIRTIO_SND_R_CHMAP_INFO, start_id, count,
+ jack_info) {}
+
+void JackInfoCommand::Reply(AudioStatus status,
+ const std::vector<virtio_snd_jack_info>& reply) {
+ MarkReplied(status);
+ if (status != AudioStatus::VIRTIO_SND_S_OK) {
+ return;
+ }
+ CHECK(reply.size() == count())
+ << "Returned unmatching info count: " << reply.size() << " vs "
+ << count();
+ for (int i = 0; i < reply.size(); ++i) {
+ info_reply()[i] = reply[i];
+ }
+}
+
+ChmapInfoCommand::ChmapInfoCommand(uint32_t start_id, size_t count,
+ virtio_snd_chmap_info* chmap_info)
+ : InfoCommand(AudioCommandType::VIRTIO_SND_R_CHMAP_INFO, start_id, count,
+ chmap_info) {}
+
+void ChmapInfoCommand::Reply(AudioStatus status,
+ const std::vector<virtio_snd_chmap_info>& reply) {
+ MarkReplied(status);
+ if (status != AudioStatus::VIRTIO_SND_S_OK) {
+ return;
+ }
+ CHECK(reply.size() == count())
+ << "Returned unmatching info count: " << reply.size() << " vs "
+ << count();
+ for (int i = 0; i < reply.size(); ++i) {
+ info_reply()[i].hdr.hda_fn_nid = Le32(reply[i].hdr.hda_fn_nid);
+ info_reply()[i].direction = reply[i].direction;
+ auto channels = std::min(VIRTIO_SND_CHMAP_MAX_SIZE, reply[i].channels);
+ info_reply()[i].channels = channels;
+ for (int j = 0; j < channels; ++j) {
+ info_reply()[i].positions[j] = reply[i].positions[j];
+ }
+ }
+}
+
StreamInfoCommand::StreamInfoCommand(uint32_t start_id, size_t count,
virtio_snd_pcm_info* pcm_info)
: InfoCommand(AudioCommandType::VIRTIO_SND_R_PCM_INFO, start_id, count,
diff --git a/host/libs/audio_connector/commands.h b/host/libs/audio_connector/commands.h
index 849dc12..1ae2e6b 100644
--- a/host/libs/audio_connector/commands.h
+++ b/host/libs/audio_connector/commands.h
@@ -60,6 +60,24 @@
R* info_reply_;
};
+class ChmapInfoCommand : public InfoCommand<virtio_snd_chmap_info> {
+ public:
+ ChmapInfoCommand(uint32_t start_id, size_t count,
+ virtio_snd_chmap_info* chmap_info);
+
+ void Reply(AudioStatus status,
+ const std::vector<virtio_snd_chmap_info>& reply);
+};
+
+class JackInfoCommand : public InfoCommand<virtio_snd_jack_info> {
+ public:
+ JackInfoCommand(uint32_t start_id, size_t count,
+ virtio_snd_jack_info* jack_info);
+
+ void Reply(AudioStatus status,
+ const std::vector<virtio_snd_jack_info>& reply);
+};
+
class StreamInfoCommand : public InfoCommand<virtio_snd_pcm_info> {
public:
StreamInfoCommand(uint32_t start_id, size_t count,
diff --git a/host/libs/audio_connector/server.cpp b/host/libs/audio_connector/server.cpp
index fd7e107..4f0570c 100644
--- a/host/libs/audio_connector/server.cpp
+++ b/host/libs/audio_connector/server.cpp
@@ -21,6 +21,7 @@
#include <unistd.h>
#include <utility>
+#include <vector>
#include <android-base/logging.h>
@@ -244,8 +245,38 @@
executor.StopStream(cmd);
return CmdReply(cmd.status());
}
- case AudioCommandType::VIRTIO_SND_R_CHMAP_INFO:
- case AudioCommandType::VIRTIO_SND_R_JACK_INFO:
+ case AudioCommandType::VIRTIO_SND_R_CHMAP_INFO: {
+ if (recv_size < sizeof(virtio_snd_query_info)) {
+ LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
+ return false;
+ }
+ auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
+ auto info_count = query_info->count.as_uint32_t();
+ auto start_id = query_info->start_id.as_uint32_t();
+ std::unique_ptr<virtio_snd_chmap_info[]> reply(
+ new virtio_snd_chmap_info[info_count]);
+ ChmapInfoCommand cmd(start_id, info_count, reply.get());
+
+ executor.ChmapsInfo(cmd);
+ return CmdReply(cmd.status(), reply.get(),
+ info_count * sizeof(reply[0]));
+ }
+ case AudioCommandType::VIRTIO_SND_R_JACK_INFO: {
+ if (recv_size < sizeof(virtio_snd_query_info)) {
+ LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
+ return false;
+ }
+ auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
+ auto info_count = query_info->count.as_uint32_t();
+ auto start_id = query_info->start_id.as_uint32_t();
+ std::unique_ptr<virtio_snd_jack_info[]> reply(
+ new virtio_snd_jack_info[info_count]);
+ JackInfoCommand cmd(start_id, info_count, reply.get());
+
+ executor.JacksInfo(cmd);
+ return CmdReply(cmd.status(), reply.get(),
+ info_count * sizeof(reply[0]));
+ }
case AudioCommandType::VIRTIO_SND_R_JACK_REMAP:
LOG(ERROR) << "Unsupported command type: " << cmd_hdr->code.as_uint32_t();
return CmdReply(AudioStatus::VIRTIO_SND_S_NOT_SUPP);
@@ -302,21 +333,17 @@
virtio_snd_hdr vio_status = {
.code = Le32(static_cast<uint32_t>(status)),
};
- auto status_sent = control_socket_->Send(&vio_status, sizeof(vio_status), 0);
- if (status_sent < sizeof(vio_status)) {
+ std::vector<uint8_t> buffer(sizeof(vio_status) + size, 0);
+ std::memcpy(buffer.data(), &vio_status, sizeof(vio_status));
+ if (data) {
+ std::memcpy(buffer.data() + sizeof(vio_status), data, size);
+ }
+ auto status_sent = control_socket_->Send(buffer.data(), buffer.size(), 0);
+ if (status_sent < sizeof(vio_status) + size) {
LOG(ERROR) << "Failed to send entire command status: "
<< control_socket_->StrError();
return false;
}
- if (status != AudioStatus::VIRTIO_SND_S_OK || size == 0) {
- return true;
- }
- auto payload_sent = control_socket_->Send(data, size, 0);
- if (payload_sent < size) {
- LOG(ERROR) << "Failed to send entire command response payload: "
- << control_socket_->StrError();
- return false;
- }
return true;
}
@@ -341,7 +368,7 @@
ssize_t AudioClientConnection::ReceiveMsg(SharedFD socket, void* buffer,
size_t size) {
auto read = socket->Recv(buffer, size, MSG_TRUNC);
- CHECK(read <= size)
+ CHECK(read < 0 || read <= size)
<< "Received a msg bigger than the buffer, msg was truncated: " << read
<< " vs " << size;
if (read == 0) {
diff --git a/host/libs/audio_connector/server.h b/host/libs/audio_connector/server.h
index 4ba2e7c..6fa14d9 100644
--- a/host/libs/audio_connector/server.h
+++ b/host/libs/audio_connector/server.h
@@ -41,6 +41,8 @@
virtual void ReleaseStream(StreamControlCommand& cmd) = 0;
virtual void StartStream(StreamControlCommand& cmd) = 0;
virtual void StopStream(StreamControlCommand& cmd) = 0;
+ virtual void ChmapsInfo(ChmapInfoCommand& cmd) = 0;
+ virtual void JacksInfo(JackInfoCommand& cmd) = 0;
// Implementations must call buffer.SendStatus() before destroying the buffer
// to notify the other side of the release of the buffer. Failure to do so
diff --git a/host/libs/audio_connector/shm_layout.h b/host/libs/audio_connector/shm_layout.h
index 9148cb8..24000e6 100644
--- a/host/libs/audio_connector/shm_layout.h
+++ b/host/libs/audio_connector/shm_layout.h
@@ -51,7 +51,7 @@
NOT_SET = static_cast<uint32_t>(-1),
};
-enum class AudioStreamDirection : uint32_t {
+enum class AudioStreamDirection : uint8_t {
VIRTIO_SND_D_OUTPUT = 0,
VIRTIO_SND_D_INPUT
};
@@ -104,6 +104,47 @@
VIRTIO_SND_PCM_RATE_384000
};
+/* standard channel position definition */
+enum AudioChannelMap : uint8_t {
+ VIRTIO_SND_CHMAP_NONE = 0, /* undefined */
+ VIRTIO_SND_CHMAP_NA, /* silent */
+ VIRTIO_SND_CHMAP_MONO, /* mono stream */
+ VIRTIO_SND_CHMAP_FL, /* front left */
+ VIRTIO_SND_CHMAP_FR, /* front right */
+ VIRTIO_SND_CHMAP_RL, /* rear left */
+ VIRTIO_SND_CHMAP_RR, /* rear right */
+ VIRTIO_SND_CHMAP_FC, /* front center */
+ VIRTIO_SND_CHMAP_LFE, /* low frequency (LFE) */
+ VIRTIO_SND_CHMAP_SL, /* side left */
+ VIRTIO_SND_CHMAP_SR, /* side right */
+ VIRTIO_SND_CHMAP_RC, /* rear center */
+ VIRTIO_SND_CHMAP_FLC, /* front left center */
+ VIRTIO_SND_CHMAP_FRC, /* front right center */
+ VIRTIO_SND_CHMAP_RLC, /* rear left center */
+ VIRTIO_SND_CHMAP_RRC, /* rear right center */
+ VIRTIO_SND_CHMAP_FLW, /* front left wide */
+ VIRTIO_SND_CHMAP_FRW, /* front right wide */
+ VIRTIO_SND_CHMAP_FLH, /* front left high */
+ VIRTIO_SND_CHMAP_FCH, /* front center high */
+ VIRTIO_SND_CHMAP_FRH, /* front right high */
+ VIRTIO_SND_CHMAP_TC, /* top center */
+ VIRTIO_SND_CHMAP_TFL, /* top front left */
+ VIRTIO_SND_CHMAP_TFR, /* top front right */
+ VIRTIO_SND_CHMAP_TFC, /* top front center */
+ VIRTIO_SND_CHMAP_TRL, /* top rear left */
+ VIRTIO_SND_CHMAP_TRR, /* top rear right */
+ VIRTIO_SND_CHMAP_TRC, /* top rear center */
+ VIRTIO_SND_CHMAP_TFLC, /* top front left center */
+ VIRTIO_SND_CHMAP_TFRC, /* top front right center */
+ VIRTIO_SND_CHMAP_TSL, /* top side left */
+ VIRTIO_SND_CHMAP_TSR, /* top side right */
+ VIRTIO_SND_CHMAP_LLFE, /* left LFE */
+ VIRTIO_SND_CHMAP_RLFE, /* right LFE */
+ VIRTIO_SND_CHMAP_BC, /* bottom center */
+ VIRTIO_SND_CHMAP_BLC, /* bottom left center */
+ VIRTIO_SND_CHMAP_BRC /* bottom right center */
+};
+
struct virtio_snd_hdr {
Le32 code;
};
@@ -119,6 +160,29 @@
Le32 hda_fn_nid;
};
+/* supported jack features */
+enum AudioJackFeatures: uint8_t {
+ VIRTIO_SND_JACK_F_REMAP = 0
+};
+
+struct virtio_snd_jack_info {
+ struct virtio_snd_info hdr;
+ Le32 features; /* 1 << VIRTIO_SND_JACK_F_XXX */
+ Le32 hda_reg_defconf;
+ Le32 hda_reg_caps;
+ uint8_t connected;
+
+ uint8_t padding[7];
+};
+
+constexpr uint8_t VIRTIO_SND_CHMAP_MAX_SIZE = 18;
+struct virtio_snd_chmap_info {
+ struct virtio_snd_info hdr;
+ uint8_t direction;
+ uint8_t channels;
+ uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
+};
+
struct virtio_snd_pcm_info {
struct virtio_snd_info hdr;
Le32 features; /* 1 << VIRTIO_SND_PCM_F_XXX */
@@ -157,7 +221,7 @@
};
// Update this value when the msg layouts change
-const uint32_t VIOS_VERSION = 1;
+const uint32_t VIOS_VERSION = 2;
struct VioSConfig {
uint32_t version;
@@ -182,6 +246,8 @@
#define ASSERT_VALID_MSG_TYPE(T, size) \
static_assert(sizeof(T) == (size), #T " has the wrong size")
ASSERT_VALID_MSG_TYPE(virtio_snd_query_info, 16);
+ASSERT_VALID_MSG_TYPE(virtio_snd_jack_info, 24);
+ASSERT_VALID_MSG_TYPE(virtio_snd_chmap_info, 24);
ASSERT_VALID_MSG_TYPE(virtio_snd_pcm_info, 32);
ASSERT_VALID_MSG_TYPE(virtio_snd_pcm_set_params, 24);
ASSERT_VALID_MSG_TYPE(virtio_snd_pcm_hdr, 8);
@@ -189,4 +255,4 @@
ASSERT_VALID_MSG_TYPE(IoStatusMsg, 16);
#undef ASSERT_VALID_MSG_TYPE
-} // namespace cuttlefish
\ No newline at end of file
+} // namespace cuttlefish
diff --git a/host/libs/config/Android.bp b/host/libs/config/Android.bp
index 3697f4e..93c1fdd 100644
--- a/host/libs/config/Android.bp
+++ b/host/libs/config/Android.bp
@@ -20,11 +20,13 @@
cc_library_static {
name: "libcuttlefish_host_config",
srcs: [
+ "adb_config.cpp",
"bootconfig_args.cpp",
"custom_actions.cpp",
"cuttlefish_config.cpp",
"cuttlefish_config_instance.cpp",
"data_image.cpp",
+ "feature.cpp",
"fetcher_config.cpp",
"host_tools_version.cpp",
"kernel_args.cpp",
@@ -35,9 +37,32 @@
"libcuttlefish_fs",
"libcuttlefish_utils",
"libbase",
+ "libfruit",
"libgflags",
"libjsoncpp",
"libz",
],
defaults: ["cuttlefish_host"],
}
+
+cc_test_host {
+ name: "libcuttlefish_host_config_test",
+ srcs: [
+ "adb_config_test.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcuttlefish_fs",
+ "libcuttlefish_host_config",
+ "libcuttlefish_utils",
+ ],
+ shared_libs: [
+ "libfruit",
+ "libjsoncpp",
+ "liblog",
+ ],
+ defaults: ["cuttlefish_host"],
+ test_options: {
+ unit_test: true,
+ },
+}
diff --git a/host/libs/config/adb_config.cpp b/host/libs/config/adb_config.cpp
new file mode 100644
index 0000000..0f8f407
--- /dev/null
+++ b/host/libs/config/adb_config.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/libs/config/adb_config.h"
+
+#include <android-base/logging.h>
+#include <fruit/fruit.h>
+#include <algorithm>
+#include <set>
+
+#include "host/libs/config/config_fragment.h"
+
+namespace cuttlefish {
+
+AdbConfig::AdbConfig() = default;
+
+static AdbMode StringToAdbMode(std::string mode) {
+ std::transform(mode.begin(), mode.end(), mode.begin(), ::tolower);
+ if (mode == "vsock_tunnel") {
+ return AdbMode::VsockTunnel;
+ } else if (mode == "vsock_half_tunnel") {
+ return AdbMode::VsockHalfTunnel;
+ } else if (mode == "native_vsock") {
+ return AdbMode::NativeVsock;
+ } else {
+ return AdbMode::Unknown;
+ }
+}
+
+static std::string AdbModeToString(AdbMode mode) {
+ switch (mode) {
+ case AdbMode::VsockTunnel:
+ return "vsock_tunnel";
+ case AdbMode::VsockHalfTunnel:
+ return "vsock_half_tunnel";
+ case AdbMode::NativeVsock:
+ return "native_vsock";
+ case AdbMode::Unknown: // fall through
+ default:
+ return "unknown";
+ }
+}
+
+void AdbConfig::set_adb_mode(const std::set<std::string>& modes) {
+ adb_mode_.clear();
+ for (auto& mode : modes) {
+ adb_mode_.insert(StringToAdbMode(mode));
+ }
+}
+void AdbConfig::set_adb_mode(const std::set<AdbMode>& modes) {
+ adb_mode_ = modes;
+}
+std::set<AdbMode> AdbConfig::adb_mode() const { return adb_mode_; }
+
+void AdbConfig::set_run_adb_connector(bool run_adb_connector) {
+ run_adb_connector_ = run_adb_connector;
+}
+bool AdbConfig::run_adb_connector() const { return run_adb_connector_; }
+
+std::string AdbConfig::Name() const { return "adb"; }
+
+constexpr char kMode[] = "mode";
+constexpr char kConnectorEnabled[] = "connector_enabled";
+Json::Value AdbConfig::Serialize() const {
+ Json::Value json;
+ json[kMode] = Json::Value(Json::arrayValue);
+ for (const auto& mode : adb_mode_) {
+ json[kMode].append(AdbModeToString(mode));
+ }
+ json[kConnectorEnabled] = run_adb_connector_;
+ return json;
+}
+bool AdbConfig::Deserialize(const Json::Value& json) {
+ if (!json.isMember(kMode) || json[kMode].type() != Json::arrayValue) {
+ LOG(ERROR) << "Invalid value for " << kMode;
+ return false;
+ }
+ adb_mode_.clear();
+ for (auto& mode : json[kMode]) {
+ if (mode.type() != Json::stringValue) {
+ LOG(ERROR) << "Invalid mode type" << mode;
+ return false;
+ }
+ adb_mode_.insert(StringToAdbMode(mode.asString()));
+ }
+ if (!json.isMember(kConnectorEnabled) ||
+ json[kConnectorEnabled].type() != Json::booleanValue) {
+ LOG(ERROR) << "Invalid value for " << kConnectorEnabled;
+ return false;
+ }
+ run_adb_connector_ = json[kConnectorEnabled].asBool();
+ return true;
+}
+
+fruit::Component<AdbConfig> AdbConfigComponent() {
+ return fruit::createComponent().addMultibinding<ConfigFragment, AdbConfig>();
+}
+
+} // namespace cuttlefish
diff --git a/host/libs/config/adb_config.h b/host/libs/config/adb_config.h
new file mode 100644
index 0000000..6306f8c
--- /dev/null
+++ b/host/libs/config/adb_config.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.
+ */
+#pragma once
+
+#include <fruit/fruit.h>
+#include <set>
+
+#include "host/libs/config/config_fragment.h"
+
+namespace cuttlefish {
+
+enum class AdbMode {
+ VsockTunnel,
+ VsockHalfTunnel,
+ NativeVsock,
+ Unknown,
+};
+
+class AdbConfig : public ConfigFragment {
+ public:
+ INJECT(AdbConfig());
+
+ void set_adb_mode(const std::set<std::string>& modes);
+ void set_adb_mode(const std::set<AdbMode>& modes);
+ std::set<AdbMode> adb_mode() const;
+
+ void set_run_adb_connector(bool run_adb_connector);
+ bool run_adb_connector() const;
+
+ // ConfigFragment
+ std::string Name() const override;
+ Json::Value Serialize() const override;
+ bool Deserialize(const Json::Value&) override;
+
+ private:
+ std::set<AdbMode> adb_mode_;
+ bool run_adb_connector_;
+};
+
+fruit::Component<AdbConfig> AdbConfigComponent();
+
+} // namespace cuttlefish
diff --git a/host/libs/config/adb_config_test.cpp b/host/libs/config/adb_config_test.cpp
new file mode 100644
index 0000000..ffef2ba
--- /dev/null
+++ b/host/libs/config/adb_config_test.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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/config/adb_config.h>
+
+#include <gtest/gtest.h>
+#include <string>
+
+namespace cuttlefish {
+
+TEST(AdbConfigTest, SaveRetrieve) {
+ AdbConfig config;
+ std::set<AdbMode> modes = {AdbMode::VsockTunnel, AdbMode::VsockHalfTunnel,
+ AdbMode::NativeVsock, AdbMode::Unknown};
+ config.set_adb_mode(modes);
+ config.set_run_adb_connector(true);
+
+ ASSERT_EQ(config.adb_mode(), modes);
+ ASSERT_TRUE(config.run_adb_connector());
+}
+
+TEST(AdbConfigTest, SerializeDeserialize) {
+ AdbConfig config;
+ config.set_adb_mode({AdbMode::VsockTunnel, AdbMode::VsockHalfTunnel,
+ AdbMode::NativeVsock, AdbMode::Unknown});
+ config.set_run_adb_connector(true);
+
+ AdbConfig config2;
+ ASSERT_TRUE(config2.Deserialize(config.Serialize()));
+ ASSERT_EQ(config.adb_mode(), config2.adb_mode());
+ ASSERT_EQ(config.run_adb_connector(), config2.run_adb_connector());
+}
+
+} // namespace cuttlefish
diff --git a/host/libs/config/bootconfig_args.cpp b/host/libs/config/bootconfig_args.cpp
index c2bc1eb..4f493ea 100644
--- a/host/libs/config/bootconfig_args.cpp
+++ b/host/libs/config/bootconfig_args.cpp
@@ -24,6 +24,7 @@
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/known_paths.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"
@@ -47,15 +48,6 @@
return os.str();
}
-std::string mac_to_str(const std::array<unsigned char, 6>& mac) {
- std::ostringstream stream;
- stream << std::hex << (int)mac[0];
- for (int i = 1; i < 6; i++) {
- stream << ":" << std::hex << (int)mac[i];
- }
- return stream.str();
-}
-
// TODO(schuffelen): Move more of this into host/libs/vm_manager, as a
// substitute for the vm_manager comparisons.
std::vector<std::string> VmManagerBootconfig(const CuttlefishConfig& config) {
@@ -90,7 +82,13 @@
bootconfig_args.push_back(
concat("androidboot.serialno=", instance.serial_number()));
- bootconfig_args.push_back(concat("androidboot.lcd_density=", config.dpi()));
+
+ // TODO(b/131884992): update to specify multiple once supported.
+ const auto display_configs = config.display_configs();
+ CHECK_GE(display_configs.size(), 1);
+ bootconfig_args.push_back(
+ concat("androidboot.lcd_density=", display_configs[0].dpi));
+
bootconfig_args.push_back(
concat("androidboot.setupwizard_mode=", config.setupwizard_mode()));
if (!config.guest_enforce_security()) {
@@ -120,7 +118,7 @@
if (config.enable_vehicle_hal_grpc_server() &&
instance.vehicle_hal_server_port() &&
- FileExists(config.vehicle_hal_grpc_server_binary())) {
+ FileExists(VehicleHalGrpcServerBinary())) {
constexpr int vehicle_hal_server_cid = 2;
bootconfig_args.push_back(concat(
"androidboot.vendor.vehiclehal.server.cid=", vehicle_hal_server_cid));
@@ -143,15 +141,21 @@
instance.frames_server_port()));
}
+ if (instance.camera_server_port()) {
+ bootconfig_args.push_back(concat("androidboot.vsock_camera_port=",
+ instance.camera_server_port()));
+ bootconfig_args.push_back(
+ concat("androidboot.vsock_camera_cid=", instance.vsock_guest_cid()));
+ }
+
if (config.enable_modem_simulator() &&
instance.modem_simulator_ports() != "") {
bootconfig_args.push_back(concat("androidboot.modem_simulator_ports=",
instance.modem_simulator_ports()));
}
- // TODO(b/158131610): Set this in crosvm instead
- bootconfig_args.push_back(concat("androidboot.wifi_mac_address=",
- mac_to_str(instance.wifi_mac_address())));
+ bootconfig_args.push_back(
+ concat("androidboot.wifi_mac_prefix=", instance.wifi_mac_prefix()));
bootconfig_args.push_back("androidboot.verifiedbootstate=orange");
diff --git a/common/libs/utils/size_utils.cpp b/host/libs/config/config_fragment.h
similarity index 64%
copy from common/libs/utils/size_utils.cpp
copy to host/libs/config/config_fragment.h
index 9f25445..cc1bfc1 100644
--- a/common/libs/utils/size_utils.cpp
+++ b/host/libs/config/config_fragment.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -13,16 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-#include "common/libs/utils/size_utils.h"
-
-#include <unistd.h>
+#include <json/json.h>
+#include <memory>
+#include <string>
namespace cuttlefish {
-uint64_t AlignToPowerOf2(uint64_t val, uint8_t align_log) {
- uint64_t align = 1ULL << align_log;
- return ((val + (align - 1)) / align) * align;
-}
+class ConfigFragment {
+ public:
+ virtual ~ConfigFragment();
+
+ virtual std::string Name() const = 0;
+ virtual Json::Value Serialize() const = 0;
+ virtual bool Deserialize(const Json::Value&) = 0;
+};
} // namespace cuttlefish
diff --git a/host/libs/config/custom_actions.cpp b/host/libs/config/custom_actions.cpp
index 2c78eba..1cc2e06 100644
--- a/host/libs/config/custom_actions.cpp
+++ b/host/libs/config/custom_actions.cpp
@@ -29,6 +29,9 @@
const char* kCustomActionShellCommand = "shell_command";
const char* kCustomActionServer = "server";
+const char* kCustomActionDeviceStates = "device_states";
+const char* kCustomActionDeviceStateLidSwitchOpen = "lid_switch_open";
+const char* kCustomActionDeviceStateHingeAngleValue = "hinge_angle_value";
const char* kCustomActionButton = "button";
const char* kCustomActionButtons = "buttons";
const char* kCustomActionButtonCommand = "command";
@@ -39,11 +42,15 @@
CustomActionConfig::CustomActionConfig(const Json::Value& dictionary) {
+ if (dictionary.isMember(kCustomActionShellCommand) +
+ dictionary.isMember(kCustomActionServer) +
+ dictionary.isMember(kCustomActionDeviceStates) !=
+ 1) {
+ LOG(FATAL) << "Custom action must contain exactly one of shell_command, "
+ << "server, or device_states";
+ return;
+ }
if (dictionary.isMember(kCustomActionShellCommand)) {
- if (dictionary.isMember(kCustomActionServer)) {
- LOG(ERROR) << "Custom action contains both shell command and action server.";
- return;
- }
// Shell command with one button.
Json::Value button_entry = dictionary[kCustomActionButton];
buttons = {{button_entry[kCustomActionButtonCommand].asString(),
@@ -60,8 +67,29 @@
buttons.push_back(button);
}
server = dictionary[kCustomActionServer].asString();
+ } else if (dictionary.isMember(kCustomActionDeviceStates)) {
+ // Device state(s) with one button.
+ // Each button press cycles to the next state, then repeats to the first.
+ Json::Value button_entry = dictionary[kCustomActionButton];
+ buttons = {{button_entry[kCustomActionButtonCommand].asString(),
+ button_entry[kCustomActionButtonTitle].asString(),
+ button_entry[kCustomActionButtonIconName].asString()}};
+ for (const Json::Value& device_state_entry :
+ dictionary[kCustomActionDeviceStates]) {
+ DeviceState state;
+ if (device_state_entry.isMember(kCustomActionDeviceStateLidSwitchOpen)) {
+ state.lid_switch_open =
+ device_state_entry[kCustomActionDeviceStateLidSwitchOpen].asBool();
+ }
+ if (device_state_entry.isMember(
+ kCustomActionDeviceStateHingeAngleValue)) {
+ state.hinge_angle_value =
+ device_state_entry[kCustomActionDeviceStateHingeAngleValue].asInt();
+ }
+ device_states.push_back(state);
+ }
} else {
- LOG(ERROR) << "Unknown custom action format.";
+ LOG(FATAL) << "Unknown custom action type.";
}
}
@@ -88,8 +116,30 @@
button_entry[kCustomActionButtonIconName] = button.icon_name;
custom_action[kCustomActionButtons].append(button_entry);
}
+ } else if (!device_states.empty()) {
+ // Device state(s) with one button.
+ custom_action[kCustomActionDeviceStates] = Json::Value(Json::arrayValue);
+ for (const auto& device_state : device_states) {
+ Json::Value device_state_entry;
+ if (device_state.lid_switch_open) {
+ device_state_entry[kCustomActionDeviceStateLidSwitchOpen] =
+ *device_state.lid_switch_open;
+ }
+ if (device_state.hinge_angle_value) {
+ device_state_entry[kCustomActionDeviceStateHingeAngleValue] =
+ *device_state.hinge_angle_value;
+ }
+ custom_action[kCustomActionDeviceStates].append(device_state_entry);
+ }
+ custom_action[kCustomActionButton] = Json::Value();
+ custom_action[kCustomActionButton][kCustomActionButtonCommand] =
+ buttons[0].command;
+ custom_action[kCustomActionButton][kCustomActionButtonTitle] =
+ buttons[0].title;
+ custom_action[kCustomActionButton][kCustomActionButtonIconName] =
+ buttons[0].icon_name;
} else {
- LOG(ERROR) << "Unknown custom action type.";
+ LOG(FATAL) << "Unknown custom action type.";
}
return custom_action;
}
diff --git a/host/libs/config/custom_actions.h b/host/libs/config/custom_actions.h
index 51e73ba..6279401 100644
--- a/host/libs/config/custom_actions.h
+++ b/host/libs/config/custom_actions.h
@@ -29,6 +29,11 @@
std::string icon_name;
};
+struct DeviceState {
+ std::optional<bool> lid_switch_open;
+ std::optional<int> hinge_angle_value;
+};
+
struct CustomActionConfig {
CustomActionConfig(const Json::Value&);
Json::Value ToJson() const;
@@ -36,6 +41,7 @@
std::vector<ControlPanelButton> buttons;
std::optional<std::string> shell_command;
std::optional<std::string> server;
+ std::vector<DeviceState> device_states;
};
} // namespace cuttlefish
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index b9b04e5..6a7a5f0 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -87,6 +87,31 @@
return StringFromEnv(environment_key, default_value) + "/" + subpath;
}
+ConfigFragment::~ConfigFragment() = default;
+
+static constexpr char kFragments[] = "fragments";
+bool CuttlefishConfig::LoadFragment(ConfigFragment& fragment) const {
+ if (!dictionary_->isMember(kFragments)) {
+ LOG(ERROR) << "Fragments member was missing";
+ return false;
+ }
+ const Json::Value& json_fragments = (*dictionary_)[kFragments];
+ if (!json_fragments.isMember(fragment.Name())) {
+ LOG(ERROR) << "Could not find a fragment called " << fragment.Name();
+ return false;
+ }
+ return fragment.Deserialize(json_fragments[fragment.Name()]);
+}
+bool CuttlefishConfig::SaveFragment(const ConfigFragment& fragment) {
+ Json::Value& json_fragments = (*dictionary_)[kFragments];
+ if (json_fragments.isMember(fragment.Name())) {
+ LOG(ERROR) << "Already have a fragment called " << fragment.Name();
+ return false;
+ }
+ json_fragments[fragment.Name()] = fragment.Serialize();
+ return true;
+}
+
static constexpr char kAssemblyDir[] = "assembly_dir";
std::string CuttlefishConfig::assembly_dir() const {
return (*dictionary_)[kAssemblyDir].asString();
@@ -123,13 +148,11 @@
(*dictionary_)[kMemoryMb] = memory_mb;
}
-static constexpr char kDpi[] = "dpi";
-int CuttlefishConfig::dpi() const { return (*dictionary_)[kDpi].asInt(); }
-void CuttlefishConfig::set_dpi(int dpi) { (*dictionary_)[kDpi] = dpi; }
-
static constexpr char kDisplayConfigs[] = "display_configs";
static constexpr char kXRes[] = "x_res";
static constexpr char kYRes[] = "y_res";
+static constexpr char kDpi[] = "dpi";
+static constexpr char kRefreshRateHz[] = "refresh_rate_hz";
std::vector<CuttlefishConfig::DisplayConfig>
CuttlefishConfig::display_configs() const {
std::vector<DisplayConfig> display_configs;
@@ -137,6 +160,9 @@
DisplayConfig display_config = {};
display_config.width = display_config_json[kXRes].asInt();
display_config.height = display_config_json[kYRes].asInt();
+ display_config.dpi = display_config_json[kDpi].asInt();
+ display_config.refresh_rate_hz =
+ display_config_json[kRefreshRateHz].asInt();
display_configs.emplace_back(std::move(display_config));
}
return display_configs;
@@ -149,20 +175,14 @@
Json::Value display_config_json(Json::objectValue);
display_config_json[kXRes] = display_configs.width;
display_config_json[kYRes] = display_configs.height;
+ display_config_json[kDpi] = display_configs.dpi;
+ display_config_json[kRefreshRateHz] = display_configs.refresh_rate_hz;
display_configs_json.append(display_config_json);
}
(*dictionary_)[kDisplayConfigs] = display_configs_json;
}
-static constexpr char kRefreshRateHz[] = "refresh_rate_hz";
-int CuttlefishConfig::refresh_rate_hz() const {
- return (*dictionary_)[kRefreshRateHz].asInt();
-}
-void CuttlefishConfig::set_refresh_rate_hz(int refresh_rate_hz) {
- (*dictionary_)[kRefreshRateHz] = refresh_rate_hz;
-}
-
void CuttlefishConfig::SetPath(const std::string& key,
const std::string& path) {
if (!path.empty()) {
@@ -195,35 +215,6 @@
return (*dictionary_)[kCuttlefishEnvPath].asString();
}
-static AdbMode stringToAdbMode(std::string mode) {
- std::transform(mode.begin(), mode.end(), mode.begin(), ::tolower);
- if (mode == "vsock_tunnel") {
- return AdbMode::VsockTunnel;
- } else if (mode == "vsock_half_tunnel") {
- return AdbMode::VsockHalfTunnel;
- } else if (mode == "native_vsock") {
- return AdbMode::NativeVsock;
- } else {
- return AdbMode::Unknown;
- }
-}
-
-static constexpr char kAdbMode[] = "adb_mode";
-std::set<AdbMode> CuttlefishConfig::adb_mode() const {
- std::set<AdbMode> args_set;
- for (auto& mode : (*dictionary_)[kAdbMode]) {
- args_set.insert(stringToAdbMode(mode.asString()));
- }
- return args_set;
-}
-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;
-}
-
static SecureHal StringToSecureHal(std::string mode) {
std::transform(mode.begin(), mode.end(), mode.begin(), ::tolower);
if (mode == "keymint") {
@@ -335,14 +326,6 @@
return (*dictionary_)[kEnableVehicleHalServer].asBool();
}
-static constexpr char kVehicleHalServerBinary[] = "vehicle_hal_server_binary";
-void CuttlefishConfig::set_vehicle_hal_grpc_server_binary(const std::string& vehicle_hal_server_binary) {
- (*dictionary_)[kVehicleHalServerBinary] = vehicle_hal_server_binary;
-}
-std::string CuttlefishConfig::vehicle_hal_grpc_server_binary() const {
- return (*dictionary_)[kVehicleHalServerBinary].asString();
-}
-
static constexpr char kCustomActions[] = "custom_actions";
void CuttlefishConfig::set_custom_actions(const std::vector<CustomActionConfig>& actions) {
Json::Value actions_array(Json::arrayValue);
@@ -384,14 +367,6 @@
(*dictionary_)[kRestartSubprocesses] = restart_subprocesses;
}
-static constexpr char kRunAdbConnector[] = "run_adb_connector";
-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;
-}
-
static constexpr char kRunAsDaemon[] = "run_as_daemon";
bool CuttlefishConfig::run_as_daemon() const {
return (*dictionary_)[kRunAsDaemon].asBool();
@@ -503,6 +478,14 @@
return (*dictionary_)[kSigServerPath].asString();
}
+static constexpr char kSigServerSecure[] = "webrtc_sig_server_secure";
+void CuttlefishConfig::set_sig_server_secure(bool secure) {
+ (*dictionary_)[kSigServerSecure] = secure;
+}
+bool CuttlefishConfig::sig_server_secure() const {
+ return (*dictionary_)[kSigServerSecure].asBool();
+}
+
static constexpr char kSigServerStrict[] = "webrtc_sig_server_strict";
void CuttlefishConfig::set_sig_server_strict(bool strict) {
(*dictionary_)[kSigServerStrict] = strict;
@@ -695,6 +678,30 @@
return (*dictionary_)[kVhostNet].asBool();
}
+static constexpr char kVhostUserMac80211Hwsim[] = "vhost_user_mac80211_hwsim";
+void CuttlefishConfig::set_vhost_user_mac80211_hwsim(const std::string& path) {
+ (*dictionary_)[kVhostUserMac80211Hwsim] = path;
+}
+std::string CuttlefishConfig::vhost_user_mac80211_hwsim() const {
+ return (*dictionary_)[kVhostUserMac80211Hwsim].asString();
+}
+
+static constexpr char kApRootfsImage[] = "ap_rootfs_image";
+std::string CuttlefishConfig::ap_rootfs_image() const {
+ return (*dictionary_)[kApRootfsImage].asString();
+}
+void CuttlefishConfig::set_ap_rootfs_image(const std::string& ap_rootfs_image) {
+ (*dictionary_)[kApRootfsImage] = ap_rootfs_image;
+}
+
+static constexpr char kApKernelImage[] = "ap_kernel_image";
+std::string CuttlefishConfig::ap_kernel_image() const {
+ return (*dictionary_)[kApKernelImage].asString();
+}
+void CuttlefishConfig::set_ap_kernel_image(const std::string& ap_kernel_image) {
+ (*dictionary_)[kApKernelImage] = ap_kernel_image;
+}
+
static constexpr char kEthernet[] = "ethernet";
void CuttlefishConfig::set_ethernet(bool ethernet) {
(*dictionary_)[kEthernet] = ethernet;
@@ -818,6 +825,10 @@
return AbsolutePath(assembly_dir() + "/" + file_name);
}
+std::string CuttlefishConfig::os_composite_disk_path() const {
+ return AssemblyPath("os_composite.img");
+}
+
CuttlefishConfig::MutableInstanceSpecific CuttlefishConfig::ForInstance(int num) {
return MutableInstanceSpecific(this, std::to_string(num));
}
@@ -876,11 +887,6 @@
return prefix + str;
}
-int GetDefaultPerInstanceVsockCid() {
- constexpr int kFirstGuestCid = 3;
- return HostSupportsVsock() ? ForCurrentInstance(kFirstGuestCid) : 0;
-}
-
std::string DefaultHostArtifactsPath(const std::string& file_name) {
return (StringFromEnv("ANDROID_SOONG_HOST_OUT", StringFromEnv("HOME", ".")) + "/") +
file_name;
@@ -906,10 +912,4 @@
return supported;
}
-bool HostSupportsVsock() {
- static bool supported =
- std::system(
- "/usr/lib/cuttlefish-common/bin/capability_query.py vsock") == 0;
- return supported;
-}
} // namespace cuttlefish
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 2aafc84..2dcd043 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -26,6 +26,7 @@
#include <vector>
#include "common/libs/utils/environment.h"
+#include "host/libs/config/config_fragment.h"
#include "host/libs/config/custom_actions.h"
namespace Json {
@@ -35,9 +36,6 @@
namespace cuttlefish {
constexpr char kLogcatSerialMode[] = "serial";
constexpr char kLogcatVsockMode[] = "vsock";
-}
-
-namespace cuttlefish {
constexpr char kDefaultUuidPrefix[] = "699acfc4-c8c4-11e7-882b-5065f31dc1";
constexpr char kCuttlefishConfigEnvVarName[] = "CUTTLEFISH_CONFIG_FILE";
@@ -55,13 +53,7 @@
constexpr char kInternalDirName[] = "internal";
constexpr char kSharedDirName[] = "shared";
constexpr char kCrosvmVarEmptyDir[] = "/var/empty";
-
-enum class AdbMode {
- VsockTunnel,
- VsockHalfTunnel,
- NativeVsock,
- Unknown,
-};
+constexpr char kKernelLoadedMessage[] = "] Linux version";
enum class SecureHal {
Unknown,
@@ -84,11 +76,16 @@
// processes by passing the --config_file option.
bool SaveToFile(const std::string& file) const;
+ bool SaveFragment(const ConfigFragment&);
+ bool LoadFragment(ConfigFragment&) const;
+
std::string assembly_dir() const;
void set_assembly_dir(const std::string& assembly_dir);
std::string AssemblyPath(const std::string&) const;
+ std::string os_composite_disk_path() const;
+
std::string vm_manager() const;
void set_vm_manager(const std::string& name);
@@ -101,15 +98,11 @@
int memory_mb() const;
void set_memory_mb(int memory_mb);
- int dpi() const;
- void set_dpi(int dpi);
-
- int refresh_rate_hz() const;
- void set_refresh_rate_hz(int refresh_rate_hz);
-
struct DisplayConfig {
int width;
int height;
+ int dpi;
+ int refresh_rate_hz;
};
std::vector<DisplayConfig> display_configs() const;
@@ -124,9 +117,6 @@
void set_cuttlefish_env_path(const std::string& path);
std::string cuttlefish_env_path() const;
- void set_adb_mode(const std::set<std::string>& modes);
- std::set<AdbMode> adb_mode() const;
-
void set_secure_hals(const std::set<std::string>& hals);
std::set<SecureHal> secure_hals() const;
@@ -163,18 +153,12 @@
void set_enable_vehicle_hal_grpc_server(bool enable_vhal_server);
bool enable_vehicle_hal_grpc_server() const;
- void set_vehicle_hal_grpc_server_binary(const std::string& vhal_server_binary);
- std::string vehicle_hal_grpc_server_binary() const;
-
void set_custom_actions(const std::vector<CustomActionConfig>& actions);
std::vector<CustomActionConfig> custom_actions() 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_enable_gnss_grpc_proxy(const bool enable_gnss_grpc_proxy);
bool enable_gnss_grpc_proxy() const;
@@ -252,6 +236,11 @@
void set_sig_server_path(const std::string& path);
std::string sig_server_path() const;
+ // Whether the webrtc process should use a secure connection (WSS) to the
+ // signaling server.
+ void set_sig_server_secure(bool secure);
+ bool sig_server_secure() const;
+
// Whether the webrtc process should attempt to verify the authenticity of the
// signaling server (reject self signed certificates)
void set_sig_server_strict(bool strict);
@@ -294,6 +283,15 @@
void set_vhost_net(bool vhost_net);
bool vhost_net() const;
+ void set_vhost_user_mac80211_hwsim(const std::string& path);
+ std::string vhost_user_mac80211_hwsim() const;
+
+ void set_ap_rootfs_image(const std::string& path);
+ std::string ap_rootfs_image() const;
+
+ void set_ap_kernel_image(const std::string& path);
+ std::string ap_kernel_image() const;
+
void set_ethernet(bool ethernet);
bool ethernet() const;
@@ -362,7 +360,9 @@
// Port number to connect to the audiocontrol server on the guest
int audiocontrol_server_port() const;
// Port number to connect to the adb server on the host
- int host_port() const;
+ int adb_host_port() const;
+ // Device-specific ID to distinguish modem simulators. Must be 4 digits.
+ int modem_simulator_host_id() const;
// Port number to connect to the gnss grpc proxy server on the host
int gnss_grpc_proxy_server_port() const;
std::string adb_ip_and_port() const;
@@ -370,6 +370,8 @@
int rootcanal_hci_port() const;
int rootcanal_link_port() const;
int rootcanal_test_port() const;
+ // Port number to connect to the camera hal on the guest
+ int camera_server_port() const;
std::string rootcanal_config_file() const;
std::string rootcanal_default_commands_file() const;
@@ -396,13 +398,12 @@
std::string instance_internal_dir() const;
- std::string touch_socket_path() const;
+ std::string touch_socket_path(int screen_idx) const;
std::string keyboard_socket_path() const;
std::string switches_socket_path() const;
std::string frames_socket_path() const;
- // mock hal guest socket that will be vsock/virtio later on
- std::string confui_hal_guest_socket_path() const;
+ int confui_host_vsock_port() const;
std::string access_kregistry_path() const;
@@ -430,14 +431,10 @@
std::string sdcard_path() const;
- std::string os_composite_disk_path() const;
-
std::string persistent_composite_disk_path() const;
std::string uboot_env_image_path() const;
- std::string vendor_boot_image_path() const;
-
std::string audio_server_path() const;
// modem simulator related
@@ -451,9 +448,11 @@
bool start_webrtc_sig_server() const;
// Wifi MAC address inside the guest
- std::array<unsigned char, 6> wifi_mac_address() const;
+ int wifi_mac_prefix() const;
std::string factory_reset_protected_path() const;
+
+ std::string persistent_bootconfig_path() const;
};
// A view into an existing CuttlefishConfig object for a particular instance.
@@ -478,11 +477,14 @@
void set_keymaster_vsock_port(int keymaster_vsock_port);
void set_vehicle_hal_server_port(int vehicle_server_port);
void set_audiocontrol_server_port(int audiocontrol_server_port);
- void set_host_port(int host_port);
+ void set_adb_host_port(int adb_host_port);
+ void set_modem_simulator_host_id(int modem_simulator_id);
void set_adb_ip_and_port(const std::string& ip_port);
+ void set_confui_host_vsock_port(int confui_host_port);
void set_rootcanal_hci_port(int rootcanal_hci_port);
void set_rootcanal_link_port(int rootcanal_link_port);
void set_rootcanal_test_port(int rootcanal_test_port);
+ void set_camera_server_port(int camera_server_port);
void set_rootcanal_config_file(const std::string& rootcanal_config_file);
void set_rootcanal_default_commands_file(
const std::string& rootcanal_default_commands_file);
@@ -502,7 +504,7 @@
void set_webrtc_device_id(const std::string& id);
void set_start_webrtc_signaling_server(bool start);
// Wifi MAC address inside the guest
- void set_wifi_mac_address(const std::array<unsigned char, 6>&);
+ void set_wifi_mac_prefix(const int wifi_mac_prefix);
// Gnss grpc proxy server port inside the host
void set_gnss_grpc_proxy_server_port(int gnss_grpc_proxy_server_port);
// Gnss grpc proxy local file path
@@ -546,9 +548,6 @@
// Returns a random serial number appeneded to a given prefix.
std::string RandomSerialNumber(const std::string& prefix);
-std::string GetDefaultMempath();
-int GetDefaultPerInstanceVsockCid();
-
std::string DefaultHostArtifactsPath(const std::string& file);
std::string HostBinaryPath(const std::string& file);
std::string DefaultGuestImagePath(const std::string& file);
@@ -558,7 +557,6 @@
// Whether the host supports qemu
bool HostSupportsQemuCli();
-bool HostSupportsVsock();
// GPU modes
extern const char* const kGpuModeAuto;
diff --git a/host/libs/config/cuttlefish_config_instance.cpp b/host/libs/config/cuttlefish_config_instance.cpp
index 9bd8c8a..81cf7db 100644
--- a/host/libs/config/cuttlefish_config_instance.cpp
+++ b/host/libs/config/cuttlefish_config_instance.cpp
@@ -165,10 +165,6 @@
return AbsolutePath(PerInstancePath("sdcard.img"));
}
-std::string CuttlefishConfig::InstanceSpecific::os_composite_disk_path() const {
- return AbsolutePath(PerInstancePath("os_composite.img"));
-}
-
std::string CuttlefishConfig::InstanceSpecific::persistent_composite_disk_path()
const {
return AbsolutePath(PerInstancePath("persistent_composite.img"));
@@ -178,10 +174,6 @@
return AbsolutePath(PerInstancePath("uboot_env.img"));
}
-std::string CuttlefishConfig::InstanceSpecific::vendor_boot_image_path() const {
- return AbsolutePath(PerInstancePath("vendor_boot_repacked.img"));
-}
-
static constexpr char kMobileBridgeName[] = "mobile_bridge_name";
std::string CuttlefishConfig::InstanceSpecific::audio_server_path() const {
@@ -205,9 +197,14 @@
(*Dictionary())[kMobileTapName] = mobile_tap_name;
}
-std::string CuttlefishConfig::InstanceSpecific::confui_hal_guest_socket_path()
- const {
- return PerInstanceInternalPath("confui_mock_hal_guest.sock");
+static constexpr char kConfUiHostPort[] = "confirmation_ui_host_port";
+int CuttlefishConfig::InstanceSpecific::confui_host_vsock_port() const {
+ return (*Dictionary())[kConfUiHostPort].asInt();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_confui_host_vsock_port(
+ int port) {
+ (*Dictionary())[kConfUiHostPort] = port;
}
static constexpr char kWifiTapName[] = "wifi_tap_name";
@@ -263,12 +260,21 @@
(*Dictionary())[kUuid] = uuid;
}
-static constexpr char kHostPort[] = "host_port";
-int CuttlefishConfig::InstanceSpecific::host_port() const {
+static constexpr char kHostPort[] = "adb_host_port";
+int CuttlefishConfig::InstanceSpecific::adb_host_port() const {
return (*Dictionary())[kHostPort].asInt();
}
-void CuttlefishConfig::MutableInstanceSpecific::set_host_port(int host_port) {
- (*Dictionary())[kHostPort] = host_port;
+void CuttlefishConfig::MutableInstanceSpecific::set_adb_host_port(int port) {
+ (*Dictionary())[kHostPort] = port;
+}
+
+static constexpr char kModemSimulatorId[] = "modem_simulator_host_id";
+int CuttlefishConfig::InstanceSpecific::modem_simulator_host_id() const {
+ return (*Dictionary())[kModemSimulatorId].asInt();
+}
+void CuttlefishConfig::MutableInstanceSpecific::set_modem_simulator_host_id(
+ int id) {
+ (*Dictionary())[kModemSimulatorId] = id;
}
static constexpr char kAdbIPAndPort[] = "adb_ip_and_port";
@@ -389,6 +395,15 @@
(*Dictionary())[kRootcanalTestPort] = rootcanal_test_port;
}
+static constexpr char kCameraServerPort[] = "camera_server_port";
+int CuttlefishConfig::InstanceSpecific::camera_server_port() const {
+ return (*Dictionary())[kCameraServerPort].asInt();
+}
+void CuttlefishConfig::MutableInstanceSpecific::set_camera_server_port(
+ int camera_server_port) {
+ (*Dictionary())[kCameraServerPort] = camera_server_port;
+}
+
static constexpr char kRootcanalConfigFile[] = "rootcanal_config_file";
std::string CuttlefishConfig::InstanceSpecific::rootcanal_config_file() const {
return (*Dictionary())[kRootcanalConfigFile].asString();
@@ -429,8 +444,10 @@
return (*Dictionary())[kStartSigServer].asBool();
}
-std::string CuttlefishConfig::InstanceSpecific::touch_socket_path() const {
- return PerInstanceInternalPath("touch.sock");
+std::string CuttlefishConfig::InstanceSpecific::touch_socket_path(
+ int screen_idx) const {
+ return PerInstanceInternalPath(
+ ("touch_" + std::to_string(screen_idx) + ".sock").c_str());
}
std::string CuttlefishConfig::InstanceSpecific::keyboard_socket_path() const {
@@ -445,32 +462,24 @@
return PerInstanceInternalPath("frames.sock");
}
-static constexpr char kWifiMacAddress[] = "wifi_mac_address";
-void CuttlefishConfig::MutableInstanceSpecific::set_wifi_mac_address(
- const std::array<unsigned char, 6>& mac_address) {
- Json::Value mac_address_obj(Json::arrayValue);
- for (const auto& num : mac_address) {
- mac_address_obj.append(num);
- }
- (*Dictionary())[kWifiMacAddress] = mac_address_obj;
+static constexpr char kWifiMacPrefix[] = "wifi_mac_prefix";
+int CuttlefishConfig::InstanceSpecific::wifi_mac_prefix() const {
+ return (*Dictionary())[kWifiMacPrefix].asInt();
}
-std::array<unsigned char, 6> CuttlefishConfig::InstanceSpecific::wifi_mac_address() const {
- std::array<unsigned char, 6> mac_address{0, 0, 0, 0, 0, 0};
- auto mac_address_obj = (*Dictionary())[kWifiMacAddress];
- if (mac_address_obj.size() != 6) {
- LOG(ERROR) << kWifiMacAddress << " entry had wrong size";
- return {};
- }
- for (int i = 0; i < 6; i++) {
- mac_address[i] = mac_address_obj[i].asInt();
- }
- return mac_address;
+void CuttlefishConfig::MutableInstanceSpecific::set_wifi_mac_prefix(
+ int wifi_mac_prefix) {
+ (*Dictionary())[kWifiMacPrefix] = wifi_mac_prefix;
}
std::string CuttlefishConfig::InstanceSpecific::factory_reset_protected_path() const {
return PerInstanceInternalPath("factory_reset_protected.img");
}
+std::string CuttlefishConfig::InstanceSpecific::persistent_bootconfig_path()
+ const {
+ return PerInstanceInternalPath("bootconfig");
+}
+
std::string CuttlefishConfig::InstanceSpecific::PerInstancePath(
const char* file_name) const {
return (instance_dir() + "/") + file_name;
diff --git a/host/libs/config/data_image.cpp b/host/libs/config/data_image.cpp
index fbfc370..189433f 100644
--- a/host/libs/config/data_image.cpp
+++ b/host/libs/config/data_image.cpp
@@ -134,11 +134,13 @@
execute({"/sbin/mkfs.ext4", image});
} else if (image_fmt == "f2fs") {
auto make_f2fs_path = cuttlefish::HostBinaryPath("make_f2fs");
- execute({make_f2fs_path, "-t", image_fmt, image, "-C", "utf8",
- "-O", "compression,extra_attr", "-g", "android"});
+ execute({make_f2fs_path, "-t", image_fmt, image, "-C", "utf8", "-O",
+ "compression,extra_attr,prjquota", "-g", "android"});
} else if (image_fmt == "sdcard") {
// Reserve 1MB in the image for the MBR and padding, to simulate what
// other OSes do by default when partitioning a drive
+ off_t offset_size_bytes = 1 << 20;
+ image_size_bytes -= offset_size_bytes;
CHECK(NewfsMsdos(image, num_mb, 1) == true)
<< "Failed to create SD-Card filesystem";
// Write the MBR after the filesystem is formatted, as the formatting tools
@@ -146,8 +148,8 @@
MasterBootRecord mbr = {
.partitions = {{
.partition_type = 0xC,
- .first_lba = (std::uint32_t)1 << 20 / SECTOR_SIZE,
- .num_sectors = (std::uint32_t)image_size_bytes / SECTOR_SIZE,
+ .first_lba = (std::uint32_t) offset_size_bytes / SECTOR_SIZE,
+ .num_sectors = (std::uint32_t) image_size_bytes / SECTOR_SIZE,
}},
.boot_signature = {0x55, 0xAA},
};
diff --git a/host/libs/config/feature.cpp b/host/libs/config/feature.cpp
new file mode 100644
index 0000000..3604fb4
--- /dev/null
+++ b/host/libs/config/feature.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 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/config/feature.h"
+
+#include <unordered_set>
+
+namespace cuttlefish {
+
+Feature::~Feature() {}
+
+/* static */ bool Feature::RunSetup(const std::vector<Feature*>& features) {
+ std::unordered_set<Feature*> enabled;
+ for (const auto& feature : features) {
+ CHECK(feature != nullptr) << "Received null feature";
+ if (feature->Enabled()) {
+ enabled.insert(feature);
+ }
+ }
+ // Collect these in a vector first to trigger any obvious dependency issues.
+ std::vector<Feature*> ordered_features;
+ auto add_feature = [&ordered_features](Feature* feature) -> bool {
+ ordered_features.push_back(feature);
+ return true;
+ };
+ if (!FeatureSuperclass<Feature>::TopologicalVisit(enabled, add_feature)) {
+ LOG(ERROR) << "Dependency issue detected, not performing any setup.";
+ return false;
+ }
+ // TODO(b/189153501): This can potentially be parallelized.
+ for (auto& feature : ordered_features) {
+ LOG(DEBUG) << "Running setup for " << feature->Name();
+ if (!feature->Setup()) {
+ LOG(ERROR) << "Setup failed for " << feature->Name();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FlagFeature::ProcessFlags(const std::vector<FlagFeature*>& features,
+ std::vector<std::string>& flags) {
+ std::unordered_set<FlagFeature*> features_set(features.begin(),
+ features.end());
+ if (features_set.count(nullptr)) {
+ LOG(ERROR) << "Received null feature";
+ return false;
+ }
+ auto handle = [&flags](FlagFeature* feature) -> bool {
+ return feature->Process(flags);
+ };
+ if (!FeatureSuperclass<FlagFeature>::TopologicalVisit(features_set, handle)) {
+ LOG(ERROR) << "Unable to parse flags.";
+ return false;
+ }
+ return true;
+}
+
+bool FlagFeature::WriteGflagsHelpXml(const std::vector<FlagFeature*>& features,
+ std::ostream& out) {
+ // Lifted from external/gflags/src/gflags_reporting.cc:ShowXMLOfFlags
+ out << "<?xml version=\"1.0\"?>\n";
+ out << "<AllFlags>\n";
+ out << " <program>program</program>\n";
+ out << " <usage>usage</usage>\n";
+ for (const auto& feature : features) {
+ if (!feature) {
+ LOG(ERROR) << "Received null feature";
+ return false;
+ }
+ if (!feature->WriteGflagsCompatHelpXml(out)) {
+ LOG(ERROR) << "Failure to write xml";
+ return false;
+ }
+ }
+ out << "</AllFlags>";
+ return true;
+}
+
+} // namespace cuttlefish
diff --git a/host/libs/config/feature.h b/host/libs/config/feature.h
new file mode 100644
index 0000000..59564c0
--- /dev/null
+++ b/host/libs/config/feature.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 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>
+#include <ostream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+namespace cuttlefish {
+
+// TODO(schuffelen): Rename this "Feature"
+template <typename Subclass>
+class FeatureSuperclass {
+ public:
+ virtual ~FeatureSuperclass() = default;
+
+ virtual std::string Name() const = 0;
+ virtual std::unordered_set<Subclass*> Dependencies() const = 0;
+
+ static bool TopologicalVisit(const std::unordered_set<Subclass*>& features,
+ const std::function<bool(Subclass*)>& callback);
+};
+
+// TODO(schuffelen): Rename this "SetupFeature"
+class Feature : public virtual FeatureSuperclass<Feature> {
+ public:
+ virtual ~Feature();
+
+ static bool RunSetup(const std::vector<Feature*>& features);
+
+ virtual bool Enabled() const = 0;
+
+ protected:
+ virtual bool Setup() = 0;
+};
+
+class FlagFeature : public FeatureSuperclass<FlagFeature> {
+ public:
+ static bool ProcessFlags(const std::vector<FlagFeature*>& features,
+ std::vector<std::string>& flags);
+ static bool WriteGflagsHelpXml(const std::vector<FlagFeature*>& features,
+ std::ostream& out);
+
+ protected:
+ // Must be executed in dependency order following Dependencies(). Expected to
+ // mutate the `flags` argument to remove handled flags, and possibly introduce
+ // new flag values (e.g. from a file).
+ virtual bool Process(std::vector<std::string>& flags) = 0;
+
+ // TODO(schuffelen): Migrate the xml help to human-readable help output after
+ // the gflags migration is done.
+
+ // Write an xml fragment that is compatible with gflags' `--helpxml` format.
+ virtual bool WriteGflagsCompatHelpXml(std::ostream& out) const = 0;
+};
+
+template <typename Subclass>
+bool FeatureSuperclass<Subclass>::TopologicalVisit(
+ const std::unordered_set<Subclass*>& features,
+ const std::function<bool(Subclass*)>& callback) {
+ enum class Status { UNVISITED, VISITING, VISITED };
+ std::unordered_map<Subclass*, Status> features_status;
+ for (const auto& feature : features) {
+ features_status[feature] = Status::UNVISITED;
+ }
+ std::function<bool(Subclass*)> visit;
+ visit = [&callback, &features_status, &visit](Subclass* feature) -> bool {
+ if (features_status.count(feature) == 0) {
+ LOG(ERROR) << "Dependency edge to " << feature->Name() << " but it is not"
+ << " part of the feature graph. This feature is either "
+ << "disabled or not correctly registered.";
+ return false;
+ } else if (features_status[feature] == Status::VISITED) {
+ return true;
+ } else if (features_status[feature] == Status::VISITING) {
+ LOG(ERROR) << "Cycle detected while visiting " << feature->Name();
+ return false;
+ }
+ features_status[feature] = Status::VISITING;
+ for (const auto& dependency : feature->Dependencies()) {
+ CHECK(dependency != nullptr)
+ << "Feature " << feature->Name() << " has a null dependency.";
+ if (!visit(dependency)) {
+ LOG(ERROR) << "Error detected while visiting " << feature->Name();
+ return false;
+ }
+ }
+ features_status[feature] = Status::VISITED;
+ if (!callback(feature)) {
+ LOG(ERROR) << "Callback error on " << feature->Name();
+ return false;
+ }
+ return true;
+ };
+ for (const auto& feature : features) {
+ if (!visit(feature)) { // `visit` will log the error chain.
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace cuttlefish
diff --git a/host/libs/config/inject.h b/host/libs/config/inject.h
new file mode 100644
index 0000000..4e2a6e0
--- /dev/null
+++ b/host/libs/config/inject.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 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 <fruit/fruit.h>
+#include <type_traits>
+
+namespace cuttlefish {
+
+/**
+ * This is a template helper to add bindings for a set of implementation
+ * classes that may each be part of multiple multibindings. To be more specific,
+ * for these example classes:
+ *
+ * class ImplementationA : public IntX, IntY {};
+ * class ImplementationB : public IntY, IntZ {};
+ *
+ * can be installed with
+ *
+ * using Deps = fruit::Required<...>;
+ * using Bases = Multibindings<Deps>::Bases<IntX, IntY, IntZ>;
+ * return fruit::createComponent()
+ * .install(Bases::Impls<ImplementationA, ImplementationB>);
+ *
+ * Note that not all implementations have to implement all interfaces. Invalid
+ * combinations are filtered out at compile-time through SFINAE.
+ */
+template <typename Deps>
+struct Multibindings {
+ /* SFINAE logic for an individual interface binding. The class does implement
+ * the interface, so add a multibinding. */
+ template <typename Base, typename Impl,
+ std::enable_if_t<std::is_base_of<Base, Impl>::value, bool> = true>
+ static fruit::Component<Deps> OneBaseOneImpl() {
+ return fruit::createComponent().addMultibinding<Base, Impl>();
+ }
+ /* SFINAE logic for an individual interface binding. The class does not
+ * implement the interface, so do not add a multibinding. */
+ template <typename Base, typename Impl,
+ std::enable_if_t<!std::is_base_of<Base, Impl>::value, bool> = true>
+ static fruit::Component<Deps> OneBaseOneImpl() {
+ return fruit::createComponent();
+ }
+
+ template <typename Base>
+ struct OneBase {
+ template <typename... ImplTypes>
+ static fruit::Component<Deps> Impls() {
+ return fruit::createComponent().installComponentFunctions(
+ fruit::componentFunction(OneBaseOneImpl<Base, ImplTypes>)...);
+ }
+ };
+
+ template <typename... BaseTypes>
+ struct Bases {
+ template <typename... ImplTypes>
+ static fruit::Component<Deps> Impls() {
+ return fruit::createComponent().installComponentFunctions(
+ fruit::componentFunction(
+ OneBase<BaseTypes>::template Impls<ImplTypes...>)...);
+ }
+ };
+};
+
+} // namespace cuttlefish
diff --git a/host/libs/config/kernel_args.cpp b/host/libs/config/kernel_args.cpp
index 421950a..a90d386 100644
--- a/host/libs/config/kernel_args.cpp
+++ b/host/libs/config/kernel_args.cpp
@@ -80,7 +80,7 @@
vm_manager_cmdline.push_back("ramoops.dump_oops=1");
} else {
// crosvm requires these additional parameters on x86_64 in bootloader mode
- AppendVector(&vm_manager_cmdline, {"pci=noacpi", "reboot=k"});
+ AppendVector(&vm_manager_cmdline, {"acpi=noirq", "reboot=k"});
}
}
}
diff --git a/host/libs/config/known_paths.cpp b/host/libs/config/known_paths.cpp
index 1581a06..bd95799 100644
--- a/host/libs/config/known_paths.cpp
+++ b/host/libs/config/known_paths.cpp
@@ -64,6 +64,13 @@
return HostBinaryPath("tombstone_receiver");
}
+std::string VehicleHalGrpcServerBinary() {
+ return HostBinaryPath(
+ "android.hardware.automotive.vehicle@2.0-virtualization-grpc-server");
+}
+
+std::string VncServerBinary() { return HostBinaryPath("vnc_server"); }
+
std::string WebRtcBinary() {
return HostBinaryPath("webRTC");
}
@@ -72,8 +79,4 @@
return HostBinaryPath("webrtc_operator");
}
-std::string VncServerBinary() {
- return HostBinaryPath("vnc_server");
-}
-
} // namespace cuttlefish
diff --git a/host/libs/config/known_paths.h b/host/libs/config/known_paths.h
index 71b6253..635f1a8 100644
--- a/host/libs/config/known_paths.h
+++ b/host/libs/config/known_paths.h
@@ -30,8 +30,9 @@
std::string RootCanalBinary();
std::string SocketVsockProxyBinary();
std::string TombstoneReceiverBinary();
+std::string VehicleHalGrpcServerBinary();
+std::string VncServerBinary();
std::string WebRtcBinary();
std::string WebRtcSigServerBinary();
-std::string VncServerBinary();
} // namespace cuttlefish
diff --git a/host/libs/confui/host_mode_ctrl.h b/host/libs/confui/host_mode_ctrl.h
index afdc6e4..0950971 100644
--- a/host/libs/confui/host_mode_ctrl.h
+++ b/host/libs/confui/host_mode_ctrl.h
@@ -25,7 +25,6 @@
#include "common/libs/confui/confui.h"
#include "host/libs/confui/host_utils.h"
-using cuttlefish::confui::DebugLog;
namespace cuttlefish {
/**
* mechanism to orchestrate concurrent executions of threads
@@ -59,11 +58,11 @@
* amd64 desktop, with Linux 5.10
*/
void WaitAndroidMode() {
- DebugLog(cuttlefish::confui::thread::GetName(),
- " checking atomic Android mode");
+ ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
+ << "checking atomic Android mode";
if (atomic_mode_ == ModeType::kAndroidMode) {
- DebugLog(cuttlefish::confui::thread::GetName(),
- " returns as it is already Android mode");
+ ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
+ << "returns as it is already Android mode";
return;
}
auto check = [this]() -> bool {
@@ -71,25 +70,25 @@
};
std::unique_lock<std::mutex> lock(mode_mtx_);
and_mode_cv_.wait(lock, check);
- DebugLog(cuttlefish::confui::thread::GetName(),
- " awakes from cond var waiting for Android mode");
+ ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
+ << "awakes from cond var waiting for Android mode";
}
void SetMode(const ModeType mode) {
- DebugLog(cuttlefish::confui::thread::GetName(),
- " tries to acquire the lock in SetMode");
+ ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
+ << " tries to acquire the lock in SetMode";
std::lock_guard<std::mutex> lock(mode_mtx_);
- DebugLog(cuttlefish::confui::thread::GetName(),
- " acquired the lock in SetMode");
+ ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
+ << " acquired the lock in SetMode";
atomic_mode_ = mode;
if (atomic_mode_ == ModeType::kAndroidMode) {
- DebugLog(cuttlefish::confui::thread::GetName(),
- " signals kAndroidMode in SetMode");
+ ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
+ << " signals kAndroidMode in SetMode";
and_mode_cv_.notify_all();
return;
}
- DebugLog(cuttlefish::confui::thread::GetName(),
- " signals kConfUI_Mode in SetMode");
+ ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
+ << "signals kConfUI_Mode in SetMode";
confui_mode_cv_.notify_all();
}
diff --git a/host/libs/confui/host_renderer.cc b/host/libs/confui/host_renderer.cc
index 65b30bf..f3e0c52 100644
--- a/host/libs/confui/host_renderer.cc
+++ b/host/libs/confui/host_renderer.cc
@@ -105,7 +105,7 @@
layout_ = teeui::instantiateLayout(teeui::ConfUILayout(), ctx_);
SetLangId(lang_id);
if (auto error = UpdateTranslations()) {
- ErrorLog("Update Translation Error");
+ ConfUiLog(ERROR) << "Update Translation Error";
return false;
}
UpdateColorScheme(&ctx_);
@@ -120,7 +120,7 @@
const auto width = ScreenConnectorInfo::ScreenWidth(display_num_);
auto pos = width * y + x;
if (pos >= (height * width)) {
- ErrorLog("Rendering Out of Bound");
+ ConfUiLog(ERROR) << "Rendering Out of Bound";
return teeui::Error::OutOfBoundsDrawing;
}
const double alfa = ((color & 0xff000000) >> 24) / 255.0;
@@ -202,7 +202,7 @@
// render all components
const auto error = drawElements(layout_, draw_pixel);
if (error) {
- ErrorLog("Painting failed: ", error.code());
+ ConfUiLog(ERROR) << "Painting failed: " << error.code();
return std::nullopt;
}
@@ -232,9 +232,10 @@
}
SetConfUiMessage(confirmation_msg);
- DebugLog("Repaint Confirmation Msg with : ", prompt_);
+ ConfUiLog(DEBUG) << "Repaint Confirmation Msg with :" << prompt_;
if (auto error = std::get<LabelConfMsg>(layout_).draw(draw_pixel)) {
- ErrorLog("Repainting Confirmation Message Label failed: ", error.code());
+ ConfUiLog(ERROR) << "Repainting Confirmation Message Label failed:"
+ << error.code();
return error;
}
return teeui::Error::OK;
diff --git a/host/libs/confui/host_renderer.h b/host/libs/confui/host_renderer.h
index f44ab82..c614109 100644
--- a/host/libs/confui/host_renderer.h
+++ b/host/libs/confui/host_renderer.h
@@ -146,7 +146,8 @@
auto& label = std::get<Label>(layout_);
str = localization::lookup(TranslationId(label.textId()));
if (str == nullptr) {
- ErrorLog("Given translation_id ", label.textId(), " not found");
+ ConfUiLog(ERROR) << "Given translation_id" << label.textId()
+ << "not found";
return Error::Localization;
}
label.setText({str, str + strlen(str)});
diff --git a/host/libs/confui/host_server.cc b/host/libs/confui/host_server.cc
index c0ca97a..c3677d8 100644
--- a/host/libs/confui/host_server.cc
+++ b/host/libs/confui/host_server.cc
@@ -22,6 +22,7 @@
#include <tuple>
#include "common/libs/confui/confui.h"
+#include "common/libs/fs/shared_buf.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/confui/host_utils.h"
@@ -33,8 +34,8 @@
return config->ForDefaultInstance();
}
-static std::string HalGuestSocketPath() {
- return CuttlefishConfigDefaultInstance().confui_hal_guest_socket_path();
+static int HalHostVsockPort() {
+ return CuttlefishConfigDefaultInstance().confui_host_vsock_port();
}
HostServer& HostServer::Get(
@@ -51,18 +52,24 @@
host_mode_ctrl_(host_mode_ctrl),
screen_connector_{screen_connector},
renderer_(display_num_),
- hal_socket_path_(HalGuestSocketPath()),
- input_multiplexer_{/* max n_elems */ 20, /* n_Qs */ 2} {
- hal_cmd_q_id_ = input_multiplexer_.GetNewQueueId(); // return 0
- user_input_evt_q_id_ = input_multiplexer_.GetNewQueueId(); // return 1
+ hal_vsock_port_(HalHostVsockPort()) {
+ const size_t max_elements = 20;
+ auto ignore_new = [](ThreadSafeQueue<ConfUiMessage>::QueueImpl*) {
+ // no op, so the queue is still full, and the new item will be discarded
+ return;
+ };
+ hal_cmd_q_id_ = input_multiplexer_.RegisterQueue(
+ HostServer::Multiplexer::CreateQueue(max_elements, ignore_new));
+ user_input_evt_q_id_ = input_multiplexer_.RegisterQueue(
+ HostServer::Multiplexer::CreateQueue(max_elements, ignore_new));
}
void HostServer::Start() {
- guest_hal_socket_ = cuttlefish::SharedFD::SocketLocalServer(
- hal_socket_path_, false, SOCK_STREAM, 0666);
+ guest_hal_socket_ =
+ cuttlefish::SharedFD::VsockServer(hal_vsock_port_, SOCK_STREAM);
if (!guest_hal_socket_->IsOpen()) {
- FatalLog("Confirmation UI host service mandates a server socket",
- "to which the guest HAL to connect.");
+ ConfUiLog(FATAL) << "Confirmation UI host service mandates a server socket"
+ << "to which the guest HAL to connect.";
return;
}
auto hal_cmd_fetching = [this]() { this->HalCmdFetcherLoop(); };
@@ -70,20 +77,21 @@
hal_input_fetcher_thread_ =
thread::RunThread("HalInputLoop", hal_cmd_fetching);
main_loop_thread_ = thread::RunThread("MainLoop", main);
- DebugLog("configured internal socket based input.");
+ ConfUiLog(DEBUG) << "configured internal vsock based input.";
return;
}
void HostServer::HalCmdFetcherLoop() {
hal_cli_socket_ = EstablishHalConnection();
if (!hal_cli_socket_->IsOpen()) {
- FatalLog("Confirmation UI host service mandates connection with HAL.");
+ ConfUiLog(FATAL)
+ << "Confirmation UI host service mandates connection with HAL.";
return;
}
while (true) {
auto opted_msg = RecvConfUiMsg(hal_cli_socket_);
if (!opted_msg) {
- ErrorLog("Error in RecvConfUiMsg from HAL");
+ ConfUiLog(ERROR) << "Error in RecvConfUiMsg from HAL";
continue;
}
auto input = std::move(opted_msg.value());
@@ -93,7 +101,7 @@
bool HostServer::SendUserSelection(UserResponse::type selection) {
if (!curr_session_) {
- FatalLog("Current session must not be null");
+ ConfUiLog(FATAL) << "Current session must not be null";
return false;
}
if (curr_session_->GetState() != MainLoopState::kInSession) {
@@ -104,8 +112,8 @@
std::lock_guard<std::mutex> lock(input_socket_mtx_);
if (selection != UserResponse::kConfirm &&
selection != UserResponse::kCancel) {
- FatalLog(selection, " must be either ", UserResponse::kConfirm, " or ",
- UserResponse::kCancel);
+ ConfUiLog(FATAL) << selection << " must be either" << UserResponse::kConfirm
+ << "or" << UserResponse::kCancel;
return false; // not reaching here
}
@@ -140,17 +148,18 @@
}
SharedFD HostServer::EstablishHalConnection() {
- DebugLog("Waiting hal accepting");
+ ConfUiLog(DEBUG) << "Waiting hal accepting";
auto new_cli = SharedFD::Accept(*guest_hal_socket_);
- DebugLog("hal client accepted");
+ ConfUiLog(DEBUG) << "hal client accepted";
return new_cli;
}
std::unique_ptr<Session> HostServer::ComputeCurrentSession(
const std::string& session_id) {
if (curr_session_ && (GetCurrentSessionId() != session_id)) {
- FatalLog(curr_session_->GetId(), " is active and in the ",
- GetCurrentState(), " but HAL sends command to ", session_id);
+ ConfUiLog(FATAL) << curr_session_->GetId() << " is active and in the"
+ << GetCurrentState() << "but HAL sends command to"
+ << session_id;
}
if (curr_session_) {
return std::move(curr_session_);
@@ -184,17 +193,18 @@
const bool is_user_input = (cmd == ConfUiCmd::kUserInputEvent);
std::string src = is_user_input ? "input" : "hal";
- DebugLog("In Session ", GetCurrentSessionId(), "m in state ",
- GetCurrentState(), " received input from ", src,
- " cmd = ", cmd_str, " and additional_info = " + additional_info,
- " going to session ", session_id);
+ ConfUiLog(DEBUG) << "In Session" << GetCurrentSessionId() << ", "
+ << "in state" << GetCurrentState() << ", "
+ << "received input from " << src << " cmd =" << cmd_str
+ << " and additional_info =" << additional_info
+ << " going to session " << session_id;
FsmInput fsm_input = ToFsmInput(input);
if (is_user_input && !curr_session_) {
// discard the input, there's no session to take it yet
// actually, no confirmation UI screen is available
- DebugLog("Took user input but no active session is available.");
+ ConfUiLog(DEBUG) << "Took user input but no active session is available.";
continue;
}
@@ -207,11 +217,12 @@
*
*/
curr_session_ = ComputeCurrentSession(session_id);
- DebugLog("Host service picked up ",
- (curr_session_ ? curr_session_->GetId() : "null session"));
- DebugLog(
- "The state of current session is ",
- curr_session_ ? ToString(curr_session_->GetState()) : "null session");
+ ConfUiLog(DEBUG) << "Host service picked up "
+ << (curr_session_ ? curr_session_->GetId()
+ : "null session");
+ ConfUiLog(DEBUG) << "The state of current session is "
+ << (curr_session_ ? ToString(curr_session_->GetState())
+ : "null session");
if (is_user_input) {
curr_session_->Transition(is_user_input, hal_cli_socket_, fsm_input,
diff --git a/host/libs/confui/host_server.h b/host/libs/confui/host_server.h
index 05164c9..b718db4 100644
--- a/host/libs/confui/host_server.h
+++ b/host/libs/confui/host_server.h
@@ -28,6 +28,7 @@
#include <android-base/logging.h>
#include <teeui/utils.h>
+#include "common/libs/concurrency/multiplexer.h"
#include "common/libs/concurrency/semaphore.h"
#include "common/libs/confui/confui.h"
#include "common/libs/fs/shared_fd.h"
@@ -36,7 +37,6 @@
#include "host/libs/confui/host_mode_ctrl.h"
#include "host/libs/confui/host_renderer.h"
#include "host/libs/confui/host_virtual_input.h"
-#include "host/libs/confui/multiplexer.h"
#include "host/libs/confui/server_common.h"
#include "host/libs/confui/session.h"
#include "host/libs/screen_connector/screen_connector.h"
@@ -154,7 +154,7 @@
ConfUiRenderer renderer_;
std::string input_socket_path_;
- std::string hal_socket_path_;
+ int hal_vsock_port_;
// session id to Session object map, for those that are suspended
std::unordered_map<std::string, std::unique_ptr<Session>> session_map_;
@@ -166,6 +166,8 @@
SharedFD hal_cli_socket_;
std::mutex input_socket_mtx_;
+ using Multiplexer =
+ Multiplexer<ConfUiMessage, ThreadSafeQueue<ConfUiMessage>>;
/*
* Multiplexer has N queues. When pop(), it is going to sleep until
* there's at least one item in at least one queue. The lower the Q
@@ -174,7 +176,7 @@
* For HostServer, we have a queue for the user input events, and
* another for hal cmd/msg queues
*/
- Multiplexer<ConfUiMessage> input_multiplexer_;
+ Multiplexer input_multiplexer_;
int hal_cmd_q_id_; // Q id in input_multiplexer_
int user_input_evt_q_id_; // Q id in input_multiplexer_
diff --git a/host/libs/confui/host_utils.cc b/host/libs/confui/host_utils.cc
index 8f4335b..6ff67b0 100644
--- a/host/libs/confui/host_utils.cc
+++ b/host/libs/confui/host_utils.cc
@@ -34,7 +34,7 @@
if (name2id_.find(name) != name2id_.end()) {
// has the name already
if (name2id_[name] != tid) { // used for another thread
- FatalLog("Thread name is duplicated.");
+ ConfUiLog(FATAL) << "Thread name is duplicated.";
}
// name and id are already set correctly
return;
diff --git a/host/libs/confui/host_utils.h b/host/libs/confui/host_utils.h
index 2e9c7eb..bd4004b 100644
--- a/host/libs/confui/host_utils.h
+++ b/host/libs/confui/host_utils.h
@@ -69,11 +69,11 @@
std::thread RunThread(const std::string& name, F&& f, Args&&... args) {
auto th = std::thread(std::forward<F>(f), std::forward<Args>(args)...);
if (name2id_.find(name) != name2id_.end()) {
- FatalLog("Thread name duplicated");
+ ConfUiLog(FATAL) << "Thread name is duplicated";
}
name2id_[name] = th.get_id();
id2name_[th.get_id()] = name;
- DebugLog(name, " thread started.");
+ ConfUiLog(DEBUG) << name << "thread started.";
return th;
}
std::string Get(const std::thread::id id = std::this_thread::get_id());
diff --git a/host/libs/confui/multiplexer.h b/host/libs/confui/multiplexer.h
deleted file mode 100644
index 2ac1928..0000000
--- a/host/libs/confui/multiplexer.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <condition_variable>
-#include <deque>
-#include <memory>
-#include <mutex>
-#include <thread>
-
-#include "common/libs/concurrency/semaphore.h"
-#include "common/libs/concurrency/thread_safe_queue.h"
-
-namespace cuttlefish {
-namespace confui {
-template <typename T>
-class Multiplexer {
- public:
- Multiplexer(int n_qs, int max_elements) : sem_items_{0}, next_{0} {
- auto drop_new = [](typename ThreadSafeQueue<T>::QueueImpl* internal_q) {
- internal_q->pop_front();
- };
- for (int i = 0; i < n_qs; i++) {
- auto queue = std::make_unique<ThreadSafeQueue<T>>(max_elements, drop_new);
- queues_.push_back(std::move(queue));
- }
- }
-
- int GetNewQueueId() {
- CHECK(next_ < queues_.size())
- << "can't get more queues than " << queues_.size();
- return next_++;
- }
-
- void Push(const int idx, T&& t) {
- CheckIdx(idx);
- queues_[idx]->Push(t);
- sem_items_.SemPost();
- }
-
- T Pop() {
- // the idx must have an item!
- // no waiting in fn()!
- sem_items_.SemWait();
- for (auto& q : queues_) {
- if (q->IsEmpty()) {
- continue;
- }
- return q->Pop();
- }
- CHECK(false) << "Multiplexer.Pop() should be able to return an item";
- // must not reach here
- return T{};
- }
-
- private:
- void CheckIdx(const int idx) {
- CHECK(idx >= 0 && idx < queues_.size()) << "queues_ array out of bound";
- }
- // total items across the queues
- Semaphore sem_items_;
- std::vector<std::unique_ptr<ThreadSafeQueue<T>>> queues_;
- int next_;
-};
-} // end of namespace confui
-} // end of namespace cuttlefish
diff --git a/host/libs/confui/server_common.cc b/host/libs/confui/server_common.cc
index b6cc208..cf46fe3 100644
--- a/host/libs/confui/server_common.cc
+++ b/host/libs/confui/server_common.cc
@@ -49,7 +49,8 @@
case ConfUiCmd::kCliAck:
case ConfUiCmd::kCliRespond:
default:
- FatalLog("The ", ToString(hal_cmd), " is not handled by Session");
+ ConfUiLog(FATAL) << "The" << ToString(hal_cmd)
+ << "is not handled by Session";
}
return FsmInput::kHalUnknown;
}
diff --git a/host/libs/confui/session.cc b/host/libs/confui/session.cc
index 92dbd7f..07015cb 100644
--- a/host/libs/confui/session.cc
+++ b/host/libs/confui/session.cc
@@ -48,9 +48,15 @@
prompt_ = msg;
locale_ = locale;
- DebugLog("actually trying to render the frame ", thread::GetName());
- auto raw_frame = reinterpret_cast<std::uint8_t*>(teeui_frame.data());
- return screen_connector_.RenderConfirmationUi(display_num_, raw_frame);
+ ConfUiLog(DEBUG) << "actually trying to render the frame"
+ << thread::GetName();
+ auto frame_width = ScreenConnectorInfo::ScreenWidth(display_num_);
+ auto frame_height = ScreenConnectorInfo::ScreenHeight(display_num_);
+ auto frame_stride_bytes =
+ ScreenConnectorInfo::ScreenStrideBytes(display_num_);
+ auto frame_bytes = reinterpret_cast<std::uint8_t*>(teeui_frame.data());
+ return screen_connector_.RenderConfirmationUi(
+ display_num_, frame_width, frame_height, frame_stride_bytes, frame_bytes);
}
bool Session::IsSuspended() const {
@@ -69,14 +75,15 @@
} break;
case MainLoopState::kWaitStop: {
if (is_user_input) {
- DebugLog("User input ignored ", ToString(fsm_input), " : ",
- additional_info, " at state ", ToString(state_));
+ ConfUiLog(DEBUG) << "User input ignored " << ToString(fsm_input)
+ << " : " << additional_info << " at the state "
+ << ToString(state_);
}
HandleWaitStop(is_user_input, hal_cli, fsm_input);
} break;
default:
// host service explicitly calls restore and suspend
- FatalLog("Must not be in the state of ", ToString(state_));
+ ConfUiLog(FATAL) << "Must not be in the state of " << ToString(state_);
break;
}
return state_;
@@ -85,11 +92,12 @@
bool Session::Suspend(SharedFD hal_cli) {
if (state_ == MainLoopState::kInit) {
// HAL sent wrong command
- FatalLog("HAL sent wrong command, suspend, when the session is in kIinit");
+ ConfUiLog(FATAL)
+ << "HAL sent wrong command, suspend, when the session is in kIinit";
return false;
}
if (state_ == MainLoopState::kSuspended) {
- DebugLog("Already kSuspended state");
+ ConfUiLog(DEBUG) << "Already kSuspended state";
return false;
}
saved_state_ = state_;
@@ -97,7 +105,7 @@
host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kAndroidMode);
if (!packet::SendAck(hal_cli, session_id_, /*is success*/ true,
"suspended")) {
- FatalLog("I/O error");
+ ConfUiLog(FATAL) << "I/O error";
return false;
}
return true;
@@ -106,30 +114,30 @@
bool Session::Restore(SharedFD hal_cli) {
if (state_ == MainLoopState::kInit) {
// HAL sent wrong command
- FatalLog("HAL sent wrong command, restore, when the session is in kIinit");
+ ConfUiLog(FATAL)
+ << "HAL sent wrong command, restore, when the session is in kIinit";
return false;
}
if (state_ != MainLoopState::kSuspended) {
- DebugLog("Already Restored to state " + ToString(state_));
+ ConfUiLog(DEBUG) << "Already Restored to state " + ToString(state_);
return false;
}
host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kConfUI_Mode);
if (!RenderDialog(prompt_, locale_)) {
// the confirmation UI is driven by a user app, not running from the start
// automatically so that means webRTC/vnc should have been set up
- ErrorLog(
- "Dialog is not rendered. However, it should. No webRTC can't initiate "
- "any confirmation UI.");
+ ConfUiLog(ERROR) << "Dialog is not rendered. However, it should."
+ << "No webRTC can't initiate any confirmation UI.";
if (!packet::SendAck(hal_cli, session_id_, false,
"render failed in restore")) {
- FatalLog("Rendering failed in restore, and ack failed in I/O");
+ ConfUiLog(FATAL) << "Rendering failed in restore, and ack failed in I/O";
}
state_ = MainLoopState::kInit;
return false;
}
if (!packet::SendAck(hal_cli, session_id_, true, "restored")) {
- FatalLog("Ack to restore failed in I/O");
+ ConfUiLog(FATAL) << "Ack to restore failed in I/O";
}
state_ = saved_state_;
saved_state_ = MainLoopState::kInit;
@@ -140,7 +148,7 @@
state_ = MainLoopState::kAwaitCleanup;
saved_state_ = MainLoopState::kInvalid;
if (!packet::SendAck(hal_cli, session_id_, true, response_msg)) {
- FatalLog("I/O error in ack to Abort");
+ ConfUiLog(FATAL) << "I/O error in ack to Abort";
return false;
}
return true;
@@ -148,7 +156,7 @@
void Session::CleanUp() {
if (state_ != MainLoopState::kAwaitCleanup) {
- FatalLog("Clean up a session only when in kAwaitCleanup");
+ ConfUiLog(FATAL) << "Clean up a session only when in kAwaitCleanup";
}
// common action done when the state is back to init state
host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kAndroidMode);
@@ -159,7 +167,7 @@
// session id
state_ = MainLoopState::kAwaitCleanup;
if (!packet::SendAck(hal_cli, session_id_, false, msg)) {
- FatalLog("I/O error in sending ack to report rendering failure");
+ ConfUiLog(FATAL) << "I/O error in sending ack to report rendering failure";
}
return;
}
@@ -176,9 +184,9 @@
return;
}
- DebugLog(ToString(fsm_input), " is handled in HandleInit");
+ ConfUiLog(DEBUG) << ToString(fsm_input) << "is handled in HandleInit";
if (fsm_input != FsmInput::kHalStart) {
- ErrorLog("invalid cmd for Init State: ", ToString(fsm_input));
+ ConfUiLog(ERROR) << "invalid cmd for Init State:" << ToString(fsm_input);
// reset the session -- destroy it & recreate it with the same
// session id
ReportErrorToHal(hal_cli, "wrong hal command");
@@ -186,20 +194,20 @@
}
// Start Session
- DebugLog("Sending ack to hal_cli: ", Enum2Base(ConfUiCmd::kCliAck));
+ ConfUiLog(DEBUG) << "Sending ack to hal_cli: "
+ << Enum2Base(ConfUiCmd::kCliAck);
host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kConfUI_Mode);
auto confirmation_msg = additional_info;
if (!RenderDialog(confirmation_msg, locale_)) {
// the confirmation UI is driven by a user app, not running from the start
// automatically so that means webRTC/vnc should have been set up
- ErrorLog(
- "Dialog is not rendered. However, it should. No webRTC can't initiate "
- "any confirmation UI.");
+ ConfUiLog(ERROR) << "Dialog is not rendered. However, it should."
+ << "No webRTC can't initiate any confirmation UI.";
ReportErrorToHal(hal_cli, "rendering failed");
return;
}
if (!packet::SendAck(hal_cli, session_id_, true, "started")) {
- FatalLog("Ack to kStart failed in I/O");
+ ConfUiLog(FATAL) << "Ack to kStart failed in I/O";
}
state_ = MainLoopState::kInSession;
return;
@@ -208,8 +216,8 @@
void Session::HandleInSession(const bool is_user_input, SharedFD hal_cli,
const FsmInput fsm_input) {
if (!is_user_input) {
- FatalLog("cmd", ToString(fsm_input),
- "should not be handled in HandleInSession");
+ ConfUiLog(FATAL) << "cmd " << ToString(fsm_input)
+ << " should not be handled in HandleInSession";
ReportErrorToHal(hal_cli, "wrong hal command");
return;
}
@@ -225,19 +233,19 @@
if (!packet::SendAck(hal_cli, session_id_, true,
"invalid user input error")) {
// note that input is what we control in memory
- PassOrDie(false, "Input must be either confirm or cancel for now.");
+ ConfUiCheck(false) << "Input must be either confirm or cancel for now.";
}
return;
}
- DebugLog("In HandlieInSession, session ", session_id_,
- " is sending the user input ", ToString(fsm_input));
+ ConfUiLog(DEBUG) << "In HandlieInSession, session " << session_id_
+ << " is sending the user input " << ToString(fsm_input);
auto selection = UserResponse::kConfirm;
if (fsm_input == FsmInput::kUserCancel) {
selection = UserResponse::kCancel;
}
if (!packet::SendResponse(hal_cli, session_id_, selection)) {
- FatalLog("I/O error in sending user response to HAL");
+ ConfUiLog(FATAL) << "I/O error in sending user response to HAL";
}
state_ = MainLoopState::kWaitStop;
return;
@@ -253,11 +261,12 @@
return;
}
if (fsm_input == FsmInput::kHalStop) {
- DebugLog("Handling Abort in kWaitStop.");
+ ConfUiLog(DEBUG) << "Handling Abort in kWaitStop.";
Kill(hal_cli, "stopped");
return;
}
- FatalLog("In WaitStop, received wrong HAL command ", ToString(fsm_input));
+ ConfUiLog(FATAL) << "In WaitStop, received wrong HAL command "
+ << ToString(fsm_input);
state_ = MainLoopState::kAwaitCleanup;
return;
}
diff --git a/host/libs/graphics_detector/Android.bp b/host/libs/graphics_detector/Android.bp
index bd6a55e..679825a 100644
--- a/host/libs/graphics_detector/Android.bp
+++ b/host/libs/graphics_detector/Android.bp
@@ -36,6 +36,7 @@
],
header_libs: [
"egl_headers",
+ "gl_headers",
"vulkan_headers",
],
shared_libs: [
@@ -44,3 +45,19 @@
],
defaults: ["cuttlefish_host"],
}
+
+cc_binary {
+ name: "detect_graphics",
+ srcs: [
+ "detect_graphics.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libcuttlefish_graphics_detector",
+ "libgflags",
+ ],
+ defaults: ["cuttlefish_host"],
+}
diff --git a/host/libs/graphics_detector/detect_graphics.cpp b/host/libs/graphics_detector/detect_graphics.cpp
new file mode 100644
index 0000000..6032520
--- /dev/null
+++ b/host/libs/graphics_detector/detect_graphics.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 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 <gflags/gflags.h>
+
+#include "android-base/logging.h"
+#include "host/libs/graphics_detector/graphics_detector.h"
+
+int main(int argc, char* argv[]) {
+ ::android::base::InitLogging(argv, android::base::StdioLogger);
+ ::android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+ LOG(INFO) << cuttlefish::GetGraphicsAvailabilityWithSubprocessCheck();
+}
\ No newline at end of file
diff --git a/host/libs/graphics_detector/graphics_detector.cpp b/host/libs/graphics_detector/graphics_detector.cpp
index 367e637..57c40f9 100644
--- a/host/libs/graphics_detector/graphics_detector.cpp
+++ b/host/libs/graphics_detector/graphics_detector.cpp
@@ -21,6 +21,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <dlfcn.h>
@@ -30,7 +31,7 @@
namespace cuttlefish {
namespace {
-constexpr const char kEglLib[] = "libEGL.so.1";
+constexpr const char kEglLib[] = "libEGL.so";
constexpr const char kGlLib[] = "libOpenGL.so.0";
constexpr const char kGles1Lib[] = "libGLESv1_CM.so.1";
constexpr const char kGles2Lib[] = "libGLESv2.so.2";
@@ -128,31 +129,6 @@
}
LOG(VERBOSE) << "Loaded eglGetDisplay.";
- EGLDisplay default_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (default_display == EGL_NO_DISPLAY) {
- LOG(VERBOSE) << "Failed to get default display. " << eglGetError();
- return;
- }
- LOG(VERBOSE) << "Found default display.";
- availability->has_egl_default_display = true;
-
- PFNEGLINITIALIZEPROC eglInitialize =
- reinterpret_cast<PFNEGLINITIALIZEPROC>(EglLoadFunction("eglInitialize"));
- if (eglInitialize == nullptr) {
- LOG(VERBOSE) << "Failed to find function eglQueryString";
- return;
- }
-
- EGLint client_version_major = 0;
- EGLint client_version_minor = 0;
- if (eglInitialize(default_display,
- &client_version_major,
- &client_version_minor) != EGL_TRUE) {
- LOG(VERBOSE) << "Failed to initialize default display.";
- return;
- }
- LOG(VERBOSE) << "Initialized default display.";
-
PFNEGLQUERYSTRINGPROC eglQueryString =
reinterpret_cast<PFNEGLQUERYSTRINGPROC>(EglLoadFunction("eglQueryString"));
if (eglQueryString == nullptr) {
@@ -161,49 +137,46 @@
}
LOG(VERBOSE) << "Loaded eglQueryString.";
- std::string client_extensions;
- if (client_version_major >= 1 && client_version_minor >= 5) {
- client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
- }
- availability->egl_client_extensions = client_extensions;
-
- EGLDisplay display = EGL_NO_DISPLAY;
-
- if (client_extensions.find("EGL_EXT_platform_base") != std::string::npos) {
- LOG(VERBOSE) << "Client extension EGL_EXT_platform_base is supported.";
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (display != EGL_NO_DISPLAY) {
+ LOG(VERBOSE) << "Found default display.";
+ } else {
+ LOG(VERBOSE) << "Failed to get default display. " << eglGetError()
+ << ". Attempting to get surfaceless display via "
+ << "eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA)";
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
EglLoadFunction("eglGetPlatformDisplayEXT"));
if (eglGetPlatformDisplayEXT == nullptr) {
LOG(VERBOSE) << "Failed to find function eglGetPlatformDisplayEXT";
- return;
+ } else {
+ display = eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA,
+ EGL_DEFAULT_DISPLAY, NULL);
}
+ }
- display =
- eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA,
- EGL_DEFAULT_DISPLAY,
- NULL);
- } else {
- LOG(VERBOSE) << "Failed to find client extension EGL_EXT_platform_base.";
- }
- if (display == EGL_NO_DISPLAY) {
- LOG(VERBOSE) << "Failed to get EGL_PLATFORM_SURFACELESS_MESA display..."
- << "failing back to EGL_DEFAULT_DISPLAY display.";
- display = default_display;
- }
if (display == EGL_NO_DISPLAY) {
LOG(VERBOSE) << "Failed to find display.";
return;
}
+ PFNEGLINITIALIZEPROC eglInitialize =
+ reinterpret_cast<PFNEGLINITIALIZEPROC>(EglLoadFunction("eglInitialize"));
+ if (eglInitialize == nullptr) {
+ LOG(VERBOSE) << "Failed to find function eglQueryString";
+ return;
+ }
+
+ EGLint client_version_major = 0;
+ EGLint client_version_minor = 0;
if (eglInitialize(display,
&client_version_major,
&client_version_minor) != EGL_TRUE) {
- LOG(VERBOSE) << "Failed to initialize surfaceless display.";
+ LOG(VERBOSE) << "Failed to initialize display.";
return;
}
- LOG(VERBOSE) << "Initialized surfaceless display.";
+ LOG(VERBOSE) << "Initialized display.";
const std::string version_string = eglQueryString(display, EGL_VERSION);
if (version_string.empty()) {
@@ -337,7 +310,46 @@
return;
}
LOG(VERBOSE) << "Make EGL context current.";
- availability->has_egl_surfaceless_with_gles = true;
+ availability->can_init_gles2_on_egl_surfaceless = true;
+
+ PFNGLGETSTRINGPROC glGetString =
+ reinterpret_cast<PFNGLGETSTRINGPROC>(eglGetProcAddress("glGetString"));
+
+ const GLubyte* gles2_vendor = glGetString(GL_VENDOR);
+ if (gles2_vendor == nullptr) {
+ LOG(VERBOSE) << "Failed to query GLES2 vendor.";
+ return;
+ }
+ const std::string gles2_vendor_string((const char*)gles2_vendor);
+ LOG(VERBOSE) << "Found GLES2 vendor: " << gles2_vendor_string;
+ availability->gles2_vendor = gles2_vendor_string;
+
+ const GLubyte* gles2_version = glGetString(GL_VERSION);
+ if (gles2_version == nullptr) {
+ LOG(VERBOSE) << "Failed to query GLES2 vendor.";
+ return;
+ }
+ const std::string gles2_version_string((const char*)gles2_version);
+ LOG(VERBOSE) << "Found GLES2 version: " << gles2_version_string;
+ availability->gles2_version = gles2_version_string;
+
+ const GLubyte* gles2_renderer = glGetString(GL_RENDERER);
+ if (gles2_renderer == nullptr) {
+ LOG(VERBOSE) << "Failed to query GLES2 renderer.";
+ return;
+ }
+ const std::string gles2_renderer_string((const char*)gles2_renderer);
+ LOG(VERBOSE) << "Found GLES2 renderer: " << gles2_renderer_string;
+ availability->gles2_renderer = gles2_renderer_string;
+
+ const GLubyte* gles2_extensions = glGetString(GL_EXTENSIONS);
+ if (gles2_extensions == nullptr) {
+ LOG(VERBOSE) << "Failed to query GLES2 extensions.";
+ return;
+ }
+ const std::string gles2_extensions_string((const char*)gles2_extensions);
+ LOG(VERBOSE) << "Found GLES2 extensions: " << gles2_extensions_string;
+ availability->gles2_extensions = gles2_extensions_string;
}
void PopulateVulkanAvailability(GraphicsAvailability* availability) {
@@ -497,6 +509,8 @@
VkPhysicalDeviceProperties device_properties = {};
vkGetPhysicalDeviceProperties(device, &device_properties);
+ LOG(VERBOSE) << "Found physical device: " << device_properties.deviceName;
+
uint32_t device_extensions_count = 0;
vkEnumerateDeviceExtensionProperties(device,
nullptr,
@@ -519,6 +533,9 @@
std::string device_extensions_string =
android::base::Join(device_extensions_strings, ' ');
+ LOG(VERBOSE) << "Found physical device extensions: "
+ << device_extensions_string;
+
if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
availability->has_discrete_gpu = true;
availability->discrete_gpu_device_name = device_properties.deviceName;
@@ -528,6 +545,18 @@
}
}
+std::string ToLower(const std::string& v) {
+ std::string result = v;
+ std::transform(result.begin(), result.end(), result.begin(),
+ [](unsigned char c) { return std::tolower(c); });
+ return result;
+}
+
+bool IsLikelySoftwareRenderer(const std::string& renderer) {
+ const std::string lower_renderer = ToLower(renderer);
+ return lower_renderer.find("llvmpipe") != std::string::npos;
+}
+
GraphicsAvailability GetGraphicsAvailability() {
GraphicsAvailability availability;
@@ -544,7 +573,8 @@
bool ShouldEnableAcceleratedRendering(
const GraphicsAvailability& availability) {
- return availability.has_egl && availability.has_egl_surfaceless_with_gles &&
+ return availability.can_init_gles2_on_egl_surfaceless &&
+ !IsLikelySoftwareRenderer(availability.gles2_renderer) &&
availability.has_discrete_gpu;
}
@@ -560,7 +590,7 @@
}
int status;
if (waitpid(pid, &status, 0) != pid) {
- PLOG(ERROR) << "Failed to wait for graphics check subprocess";
+ PLOG(VERBOSE) << "Failed to wait for graphics check subprocess";
return GraphicsAvailability{};
}
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
@@ -575,20 +605,32 @@
std::ios_base::fmtflags flags_backup(stream.flags());
stream << std::boolalpha;
stream << "Graphics Availability:\n";
- stream << "OpenGL available: " << availability.has_gl << "\n";
- stream << "OpenGL ES1 available: " << availability.has_gles1 << "\n";
- stream << "OpenGL ES2 available: " << availability.has_gles2 << "\n";
- stream << "EGL available: " << availability.has_egl << "\n";
+
+ stream << "\n";
+ stream << "OpenGL lib available: " << availability.has_gl << "\n";
+ stream << "OpenGL ES1 lib available: " << availability.has_gles1 << "\n";
+ stream << "OpenGL ES2 lib available: " << availability.has_gles2 << "\n";
+ stream << "EGL lib available: " << availability.has_egl << "\n";
+ stream << "Vulkan lib available: " << availability.has_vulkan << "\n";
+
+ stream << "\n";
stream << "EGL client extensions: " << availability.egl_client_extensions
<< "\n";
- stream << "EGL default display available: "
- << availability.has_egl_default_display << "\n";
+
+ stream << "\n";
stream << "EGL display vendor: " << availability.egl_vendor << "\n";
stream << "EGL display version: " << availability.egl_version << "\n";
stream << "EGL display extensions: " << availability.egl_extensions << "\n";
- stream << "EGL surfaceless display with GLES: "
- << availability.has_egl_surfaceless_with_gles << "\n";
- stream << "Vulkan available: " << availability.has_vulkan << "\n";
+
+ stream << "GLES2 can init on surfaceless display: "
+ << availability.can_init_gles2_on_egl_surfaceless << "\n";
+ stream << "\n";
+ stream << "GLES2 vendor: " << availability.gles2_vendor << "\n";
+ stream << "GLES2 version: " << availability.gles2_version << "\n";
+ stream << "GLES2 renderer: " << availability.gles2_renderer << "\n";
+ stream << "GLES2 extensions: " << availability.gles2_extensions << "\n";
+
+ stream << "\n";
stream << "Vulkan discrete GPU detected: " << availability.has_discrete_gpu
<< "\n";
if (availability.has_discrete_gpu) {
@@ -597,6 +639,11 @@
stream << "Vulkan discrete GPU device extensions: "
<< availability.discrete_gpu_device_extensions << "\n";
}
+
+ stream << "\n";
+ stream << "Accelerated rendering supported: "
+ << ShouldEnableAcceleratedRendering(availability);
+
stream.flags(flags_backup);
return stream;
}
diff --git a/host/libs/graphics_detector/graphics_detector.h b/host/libs/graphics_detector/graphics_detector.h
index 83a7068..1bacc3d 100644
--- a/host/libs/graphics_detector/graphics_detector.h
+++ b/host/libs/graphics_detector/graphics_detector.h
@@ -25,13 +25,20 @@
bool has_gles1 = false;
bool has_gles2 = false;
bool has_egl = false;
- bool has_egl_default_display = false;
+ bool has_vulkan = false;
+
std::string egl_client_extensions;
+
std::string egl_version;
std::string egl_vendor;
std::string egl_extensions;
- bool has_egl_surfaceless_with_gles = false;
- bool has_vulkan = false;
+
+ bool can_init_gles2_on_egl_surfaceless = false;
+ std::string gles2_vendor;
+ std::string gles2_version;
+ std::string gles2_renderer;
+ std::string gles2_extensions;
+
bool has_discrete_gpu = false;
std::string discrete_gpu_device_name;
std::string discrete_gpu_device_extensions;
diff --git a/host/libs/image_aggregator/Android.bp b/host/libs/image_aggregator/Android.bp
index 491a777..23d4874 100644
--- a/host/libs/image_aggregator/Android.bp
+++ b/host/libs/image_aggregator/Android.bp
@@ -23,7 +23,7 @@
"cdisk_spec.proto",
],
proto: {
- type: "full",
+ type: "lite",
export_proto_headers: true,
include_dirs: [
"external/protobuf/src",
@@ -37,11 +37,12 @@
srcs: [
"image_aggregator.cc",
],
+ export_include_dirs: ["."],
shared_libs: [
"libcuttlefish_fs",
"libcuttlefish_utils",
"libbase",
- "libprotobuf-cpp-full",
+ "libprotobuf-cpp-lite",
"libz",
],
static_libs: [
diff --git a/host/libs/image_aggregator/image_aggregator.cc b/host/libs/image_aggregator/image_aggregator.cc
index 8d4027a..0868aa1 100644
--- a/host/libs/image_aggregator/image_aggregator.cc
+++ b/host/libs/image_aggregator/image_aggregator.cc
@@ -31,6 +31,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <cdisk_spec.pb.h>
#include <google/protobuf/text_format.h>
#include <sparse/sparse.h>
@@ -39,6 +40,7 @@
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/cf_endian.h"
#include "common/libs/utils/files.h"
#include "common/libs/utils/size_utils.h"
#include "common/libs/utils/subprocess.h"
@@ -47,13 +49,9 @@
namespace cuttlefish {
namespace {
-// Keep the full disk size a multiple of 64k, for crosvm's virtio_blk driver
-constexpr int DISK_SIZE_SHIFT = 16;
-
-// Keep all partitions 4k aligned, for host performance reasons
-constexpr int PARTITION_SIZE_SHIFT = 12;
-
constexpr int GPT_NUM_PARTITIONS = 128;
+static const std::string CDISK_MAGIC = "composite_disk\x1d";
+static const std::string QCOW2_MAGIC = "QFI\xfb";
/**
* Creates a "Protective" MBR Partition Table header. The GUID
@@ -106,32 +104,53 @@
struct __attribute__((packed)) GptBeginning {
MasterBootRecord protective_mbr;
GptHeader header;
- std::uint8_t header_padding[420];
+ std::uint8_t header_padding[SECTOR_SIZE - sizeof(GptHeader)];
GptPartitionEntry entries[GPT_NUM_PARTITIONS];
std::uint8_t partition_alignment[3072];
};
-static_assert(sizeof(GptBeginning) == SECTOR_SIZE * 40);
+static_assert(AlignToPowerOf2(sizeof(GptBeginning), PARTITION_SIZE_SHIFT) ==
+ sizeof(GptBeginning));
struct __attribute__((packed)) GptEnd {
GptPartitionEntry entries[GPT_NUM_PARTITIONS];
GptHeader footer;
- std::uint8_t footer_padding[420];
+ std::uint8_t footer_padding[SECTOR_SIZE - sizeof(GptHeader)];
};
-static_assert(sizeof(GptEnd) == SECTOR_SIZE * 33);
+static_assert(sizeof(GptEnd) % SECTOR_SIZE == 0);
struct PartitionInfo {
- ImagePartition source;
- std::uint64_t guest_size;
- std::uint64_t host_size;
+ MultipleImagePartition source;
+ std::uint64_t size;
std::uint64_t offset;
+
+ std::uint64_t AlignedSize() const { return AlignToPartitionSize(size); }
};
+struct __attribute__((packed)) QCowHeader {
+ Be32 magic;
+ Be32 version;
+ Be64 backing_file_offset;
+ Be32 backing_file_size;
+ Be32 cluster_bits;
+ Be64 size;
+ Be32 crypt_method;
+ Be32 l1_size;
+ Be64 l1_table_offset;
+ Be64 refcount_table_offset;
+ Be32 refcount_table_clusters;
+ Be32 nb_snapshots;
+ Be64 snapshots_offset;
+};
+
+static_assert(sizeof(QCowHeader) == 72);
+
/*
- * Returns the file size of `file_path`. If `file_path` is an Android-Sparse
- * file, returns the file size it would have after being converted to a raw
- * file.
+ * Returns the expanded file size of `file_path`. Note that the raw size of
+ * files doesn't match how large they may appear inside a VM.
+ *
+ * Supported types: Composite disk image, Qcows2, Android-Sparse, Raw
*
* Android-Sparse is a file format invented by Android that optimizes for
* chunks of zeroes or repeated data. The Android build system can produce
@@ -139,15 +158,63 @@
* disk file, as the imag eflashing process also can handle Android-Sparse
* images.
*/
-std::uint64_t UnsparsedSize(const std::string& file_path) {
- auto fd = open(file_path.c_str(), O_RDONLY);
- CHECK(fd >= 0) << "Could not open \"" << file_path << "\""
- << strerror(errno);
- auto sparse = sparse_file_import(fd, /* verbose */ false, /* crc */ false);
- auto size =
- sparse ? sparse_file_len(sparse, false, true) : FileSize(file_path);
- close(fd);
- return size;
+std::uint64_t ExpandedStorageSize(const std::string& file_path) {
+ android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY));
+ CHECK(fd.get() >= 0) << "Could not open \"" << file_path << "\""
+ << strerror(errno);
+
+ std::uint64_t file_size = FileSize(file_path);
+
+ // Try to read the disk in a nicely-aligned block size unless the whole file
+ // is smaller.
+ constexpr uint64_t MAGIC_BLOCK_SIZE = 4096;
+ std::string magic(std::min(file_size, MAGIC_BLOCK_SIZE), '\0');
+ if (!android::base::ReadFully(fd, magic.data(), magic.size())) {
+ PLOG(FATAL) << "Fail to read: " << file_path;
+ return 0;
+ }
+ CHECK(lseek(fd, 0, SEEK_SET) != -1)
+ << "Fail to seek(\"" << file_path << "\")" << strerror(errno);
+
+ // Composite disk image
+ if (android::base::StartsWith(magic, CDISK_MAGIC)) {
+ // seek to the beginning of proto message
+ CHECK(lseek(fd, CDISK_MAGIC.size(), SEEK_SET) != -1)
+ << "Fail to seek(\"" << file_path << "\")" << strerror(errno);
+ std::string message;
+ if (!android::base::ReadFdToString(fd, &message)) {
+ PLOG(FATAL) << "Fail to read(cdisk): " << file_path;
+ return 0;
+ }
+ CompositeDisk cdisk;
+ if (!cdisk.ParseFromString(message)) {
+ PLOG(FATAL) << "Fail to parse(cdisk): " << file_path;
+ return 0;
+ }
+ return cdisk.length();
+ }
+
+ // Qcow2 image
+ if (android::base::StartsWith(magic, QCOW2_MAGIC)) {
+ QCowHeader header;
+ if (!android::base::ReadFully(fd, &header, sizeof(QCowHeader))) {
+ PLOG(FATAL) << "Fail to read(qcow2 header): " << file_path;
+ return 0;
+ }
+ return header.size.as_uint64_t();
+ }
+
+ // Android-Sparse
+ if (auto sparse =
+ sparse_file_import(fd, /* verbose */ false, /* crc */ false);
+ sparse) {
+ auto size = sparse_file_len(sparse, false, true);
+ sparse_file_destroy(sparse);
+ return size;
+ }
+
+ // raw image file
+ return file_size;
}
/*
@@ -165,6 +232,15 @@
}
}
+MultipleImagePartition ToMultipleImagePartition(ImagePartition source) {
+ return MultipleImagePartition{
+ .label = source.label,
+ .image_file_paths = std::vector{source.image_file_path},
+ .type = source.type,
+ .read_only = source.read_only,
+ };
+}
+
/**
* Incremental builder class for producing partition tables. Add partitions
* one-by-one, then produce specification files
@@ -174,7 +250,7 @@
std::vector<PartitionInfo> partitions_;
std::uint64_t next_disk_offset_;
- static const char *GetPartitionGUID(ImagePartition source) {
+ static const char* GetPartitionGUID(MultipleImagePartition source) {
// Due to some endianness mismatch in e2fsprogs GUID vs GPT, the GUIDs are
// rearranged to make the right GUIDs appear in gdisk
switch (source.type) {
@@ -188,28 +264,33 @@
LOG(FATAL) << "Unknown partition type: " << (int) source.type;
}
}
+
public:
CompositeDiskBuilder() : next_disk_offset_(sizeof(GptBeginning)) {}
- void AppendDisk(ImagePartition source) {
- auto host_size = UnsparsedSize(source.image_file_path);
- auto guest_size = AlignToPowerOf2(host_size, PARTITION_SIZE_SHIFT);
- CHECK(host_size == guest_size || source.read_only)
- << "read-write file " << source.image_file_path
+ void AppendPartition(ImagePartition source) {
+ AppendPartition(ToMultipleImagePartition(source));
+ }
+
+ void AppendPartition(MultipleImagePartition source) {
+ uint64_t size = 0;
+ for (const auto& path : source.image_file_paths) {
+ size += ExpandedStorageSize(path);
+ }
+ auto aligned_size = AlignToPartitionSize(size);
+ CHECK(size == aligned_size || source.read_only)
+ << "read-write partition " << source.label
<< " is not aligned to the size of " << (1 << PARTITION_SIZE_SHIFT);
partitions_.push_back(PartitionInfo{
.source = source,
- .guest_size = guest_size,
- .host_size = host_size,
+ .size = size,
.offset = next_disk_offset_,
});
- next_disk_offset_ =
- AlignToPowerOf2(next_disk_offset_ + guest_size, PARTITION_SIZE_SHIFT);
+ next_disk_offset_ = next_disk_offset_ + aligned_size;
}
std::uint64_t DiskSize() const {
- std::uint64_t val = next_disk_offset_ + sizeof(GptEnd);
- return AlignToPowerOf2(val, DISK_SIZE_SHIFT);
+ return AlignToPowerOf2(next_disk_offset_ + sizeof(GptEnd), DISK_SIZE_SHIFT);
}
/**
@@ -228,22 +309,27 @@
header->set_offset(0);
for (auto& partition : partitions_) {
- ComponentDisk* component = disk.add_component_disks();
- component->set_file_path(AbsolutePath(partition.source.image_file_path));
- component->set_offset(partition.offset);
- component->set_read_write_capability(
- partition.source.read_only ? ReadWriteCapability::READ_ONLY
- : ReadWriteCapability::READ_WRITE);
- // When partition's size differs from its size on the host
+ uint64_t size = 0;
+ for (const auto& path : partition.source.image_file_paths) {
+ ComponentDisk* component = disk.add_component_disks();
+ component->set_file_path(AbsolutePath(path));
+ component->set_offset(partition.offset + size);
+ component->set_read_write_capability(
+ partition.source.read_only ? ReadWriteCapability::READ_ONLY
+ : ReadWriteCapability::READ_WRITE);
+ size += ExpandedStorageSize(path);
+ }
+ CHECK(partition.size == size);
+ // When partition's aligned size differs from its (unaligned) size
// reading the disk within the guest os would fail due to the gap.
// Putting any disk bigger than 4K can fill this gap.
// Here we reuse the header which is always > 4K.
// We don't fill the "writable" disk's hole and it should be an error
// because writes in the guest of can't be reflected to the backing file.
- if (partition.guest_size != partition.host_size) {
+ if (partition.AlignedSize() != partition.size) {
ComponentDisk* component = disk.add_component_disks();
component->set_file_path(AbsolutePath(header_file));
- component->set_offset(partition.offset + partition.host_size);
+ component->set_offset(partition.offset + partition.size);
component->set_read_write_capability(ReadWriteCapability::READ_ONLY);
}
}
@@ -268,19 +354,20 @@
return {};
}
GptBeginning gpt = {
- .protective_mbr = ProtectiveMbr(DiskSize()),
- .header = {
- .signature = {'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T'},
- .revision = {0, 0, 1, 0},
- .header_size = sizeof(GptHeader),
- .current_lba = 1,
- .backup_lba = (next_disk_offset_ + sizeof(GptEnd)) / SECTOR_SIZE - 1,
- .first_usable_lba = sizeof(GptBeginning) / SECTOR_SIZE,
- .last_usable_lba = (next_disk_offset_ - SECTOR_SIZE) / SECTOR_SIZE,
- .partition_entries_lba = 2,
- .num_partition_entries = GPT_NUM_PARTITIONS,
- .partition_entry_size = sizeof(GptPartitionEntry),
- },
+ .protective_mbr = ProtectiveMbr(DiskSize()),
+ .header =
+ {
+ .signature = {'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T'},
+ .revision = {0, 0, 1, 0},
+ .header_size = sizeof(GptHeader),
+ .current_lba = 1,
+ .backup_lba = (DiskSize() / SECTOR_SIZE) - 1,
+ .first_usable_lba = sizeof(GptBeginning) / SECTOR_SIZE,
+ .last_usable_lba = (next_disk_offset_ / SECTOR_SIZE) - 1,
+ .partition_entries_lba = 2,
+ .num_partition_entries = GPT_NUM_PARTITIONS,
+ .partition_entry_size = sizeof(GptPartitionEntry),
+ },
};
uuid_generate(gpt.header.disk_guid);
for (std::size_t i = 0; i < partitions_.size(); i++) {
@@ -288,7 +375,7 @@
gpt.entries[i] = GptPartitionEntry{
.first_lba = partition.offset / SECTOR_SIZE,
.last_lba =
- (partition.offset + partition.guest_size) / SECTOR_SIZE - 1,
+ (partition.offset + partition.AlignedSize()) / SECTOR_SIZE - 1,
};
uuid_generate(gpt.entries[i].unique_partition_guid);
if (uuid_parse(GetPartitionGUID(partition.source),
@@ -314,9 +401,10 @@
*/
GptEnd End(const GptBeginning& head) const {
GptEnd gpt;
- std::memcpy((void*) gpt.entries, (void*) head.entries, 128 * 128);
+ std::memcpy((void*)gpt.entries, (void*)head.entries, sizeof(gpt.entries));
gpt.footer = head.header;
- gpt.footer.partition_entries_lba = next_disk_offset_ / SECTOR_SIZE;
+ gpt.footer.partition_entries_lba =
+ (DiskSize() - sizeof(gpt.entries)) / SECTOR_SIZE - 1;
std::swap(gpt.footer.current_lba, gpt.footer.backup_lba);
gpt.footer.header_crc32 = 0;
gpt.footer.header_crc32 =
@@ -334,11 +422,17 @@
return true;
}
-bool WriteEnd(SharedFD out, const GptEnd& end, std::int64_t padding) {
- std::string end_str((const char*) &end, sizeof(GptEnd));
- end_str.resize(end_str.size() + padding, '\0');
- if (WriteAll(out, end_str) != end_str.size()) {
- LOG(ERROR) << "Could not write GPT end: " << out->StrError();
+bool WriteEnd(SharedFD out, const GptEnd& end) {
+ auto disk_size = (end.footer.current_lba + 1) * SECTOR_SIZE;
+ auto footer_start = (end.footer.last_usable_lba + 1) * SECTOR_SIZE;
+ auto padding = disk_size - footer_start - sizeof(GptEnd);
+ std::string padding_str(padding, '\0');
+ if (WriteAll(out, padding_str) != padding_str.size()) {
+ LOG(ERROR) << "Could not write GPT end padding: " << out->StrError();
+ return false;
+ }
+ if (WriteAllBinary(out, &end) != sizeof(end)) {
+ LOG(ERROR) << "Could not write GPT end contents: " << out->StrError();
return false;
}
return true;
@@ -394,12 +488,16 @@
} // namespace
+uint64_t AlignToPartitionSize(uint64_t size) {
+ return AlignToPowerOf2(size, PARTITION_SIZE_SHIFT);
+}
+
void AggregateImage(const std::vector<ImagePartition>& partitions,
const std::string& output_path) {
DeAndroidSparse(partitions);
CompositeDiskBuilder builder;
- for (auto& disk : partitions) {
- builder.AppendDisk(disk);
+ for (auto& partition : partitions) {
+ builder.AppendPartition(partition);
}
auto output = SharedFD::Creat(output_path, 0600);
auto beginning = builder.Beginning();
@@ -415,8 +513,7 @@
<< "\" to \"" << output_path << "\": " << output->StrError();
}
// Handle disk images that are not aligned to PARTITION_SIZE_SHIFT
- std::uint64_t padding =
- AlignToPowerOf2(file_size, PARTITION_SIZE_SHIFT) - file_size;
+ std::uint64_t padding = AlignToPartitionSize(file_size) - file_size;
std::string padding_str;
padding_str.resize(padding, '\0');
if (WriteAll(output, padding_str) != padding_str.size()) {
@@ -424,9 +521,7 @@
<< "\": " << output->StrError();
}
}
- std::uint64_t padding =
- builder.DiskSize() - ((beginning.header.backup_lba + 1) * SECTOR_SIZE);
- if (!WriteEnd(output, builder.End(beginning), padding)) {
+ if (!WriteEnd(output, builder.End(beginning))) {
LOG(FATAL) << "Could not write GPT end to \"" << output_path
<< "\": " << output->StrError();
}
@@ -436,9 +531,21 @@
const std::string& header_file,
const std::string& footer_file,
const std::string& output_composite_path) {
+ std::vector<MultipleImagePartition> multiple_image_partitions;
+ for (const auto& partition : partitions) {
+ multiple_image_partitions.push_back(ToMultipleImagePartition(partition));
+ }
+ return CreateCompositeDisk(std::move(multiple_image_partitions), header_file,
+ footer_file, output_composite_path);
+}
+
+void CreateCompositeDisk(std::vector<MultipleImagePartition> partitions,
+ const std::string& header_file,
+ const std::string& footer_file,
+ const std::string& output_composite_path) {
CompositeDiskBuilder builder;
- for (auto& disk : partitions) {
- builder.AppendDisk(disk);
+ for (auto& partition : partitions) {
+ builder.AppendPartition(partition);
}
auto header = SharedFD::Creat(header_file, 0600);
auto beginning = builder.Beginning();
@@ -447,16 +554,14 @@
<< "\": " << header->StrError();
}
auto footer = SharedFD::Creat(footer_file, 0600);
- std::uint64_t padding =
- builder.DiskSize() - ((beginning.header.backup_lba + 1) * SECTOR_SIZE);
- if (!WriteEnd(footer, builder.End(beginning), padding)) {
+ if (!WriteEnd(footer, builder.End(beginning))) {
LOG(FATAL) << "Could not write GPT end to \"" << footer_file
<< "\": " << footer->StrError();
}
auto composite_proto = builder.MakeCompositeDiskSpec(header_file, footer_file);
std::ofstream composite(output_composite_path.c_str(),
std::ios::binary | std::ios::trunc);
- composite << "composite_disk\x1d";
+ composite << CDISK_MAGIC;
composite_proto.SerializeToOstream(&composite);
composite.flush();
}
diff --git a/host/libs/image_aggregator/image_aggregator.h b/host/libs/image_aggregator/image_aggregator.h
index 97bc13b..b9a2458 100644
--- a/host/libs/image_aggregator/image_aggregator.h
+++ b/host/libs/image_aggregator/image_aggregator.h
@@ -36,6 +36,15 @@
bool read_only;
};
+struct MultipleImagePartition {
+ std::string label;
+ std::vector<std::string> image_file_paths;
+ ImagePartitionType type;
+ bool read_only;
+};
+
+uint64_t AlignToPartitionSize(uint64_t size);
+
/**
* Combine the files in `partition` into a single raw disk file and write it to
* `output_path`. The raw disk file will have a GUID Partition Table and copy in
@@ -62,6 +71,14 @@
const std::string& output_composite_path);
/**
+ * Overloaded function to generate a composite disk with multiple images for a
+ * single partition.
+ */
+void CreateCompositeDisk(std::vector<MultipleImagePartition> partitions,
+ const std::string& header_file,
+ const std::string& footer_file,
+ const std::string& output_composite_path);
+/**
* Generate a qcow overlay backed by a given implementation file.
*
* qcow, or "QEMU Copy-On-Write" is a file format containing a list of disk
diff --git a/host/libs/screen_connector/Android.bp b/host/libs/screen_connector/Android.bp
index fcfc747..d4e4002 100644
--- a/host/libs/screen_connector/Android.bp
+++ b/host/libs/screen_connector/Android.bp
@@ -28,10 +28,18 @@
"libjsoncpp",
"liblog",
],
+ header_libs: [
+ "libcuttlefish_confui_host_headers",
+ ],
static_libs: [
"libcuttlefish_host_config",
"libcuttlefish_utils",
+ "libcuttlefish_confui",
"libcuttlefish_wayland_server",
+ "libcuttlefish_confui_host",
+ "libft2.nodep",
+ "libteeui",
+ "libteeui_localization",
],
defaults: ["cuttlefish_host"],
}
diff --git a/host/libs/screen_connector/screen_connector.h b/host/libs/screen_connector/screen_connector.h
index 86aa523..6b1c039 100644
--- a/host/libs/screen_connector/screen_connector.h
+++ b/host/libs/screen_connector/screen_connector.h
@@ -16,21 +16,26 @@
#pragma once
-#include <cassert>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
+#include <optional>
+#include <string>
#include <thread>
#include <type_traits>
#include <android-base/logging.h>
-
+#include "common/libs/confui/confui.h"
+#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/size_utils.h"
+
#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/confui/host_mode_ctrl.h"
+#include "host/libs/confui/host_utils.h"
#include "host/libs/screen_connector/screen_connector_common.h"
+#include "host/libs/screen_connector/screen_connector_multiplexer.h"
#include "host/libs/screen_connector/screen_connector_queue.h"
-#include "host/libs/screen_connector/screen_connector_ctrl.h"
#include "host/libs/screen_connector/wayland_screen_connector.h"
namespace cuttlefish {
@@ -44,6 +49,8 @@
static_assert(std::is_base_of<ScreenConnectorFrameInfo, ProcessedFrameType>::value,
"ProcessedFrameType should inherit ScreenConnectorFrameInfo");
+ using FrameMultiplexer = ScreenConnectorInputMultiplexer<ProcessedFrameType>;
+
/**
* This is the type of the callback function WebRTC/VNC is supposed to provide
* ScreenConnector with.
@@ -57,18 +64,21 @@
* call.
*/
using GenerateProcessedFrameCallback = std::function<void(
- std::uint32_t /*display_number*/, std::uint8_t* /*frame_pixels*/,
+ std::uint32_t /*display_number*/, std::uint32_t /*frame_width*/,
+ std::uint32_t /*frame_height*/, std::uint32_t /*frame_stride_bytes*/,
+ std::uint8_t* /*frame_bytes*/,
/* ScImpl enqueues this type into the Q */
ProcessedFrameType& msg)>;
- static std::unique_ptr<ScreenConnector<ProcessedFrameType>> Get(const int frames_fd) {
+ static std::unique_ptr<ScreenConnector<ProcessedFrameType>> Get(
+ const int frames_fd, HostModeCtrl& host_mode_ctrl) {
auto config = cuttlefish::CuttlefishConfig::Get();
ScreenConnector<ProcessedFrameType>* raw_ptr = nullptr;
if (config->gpu_mode() == cuttlefish::kGpuModeDrmVirgl ||
config->gpu_mode() == cuttlefish::kGpuModeGfxStream ||
config->gpu_mode() == cuttlefish::kGpuModeGuestSwiftshader) {
raw_ptr = new ScreenConnector<ProcessedFrameType>(
- std::make_unique<WaylandScreenConnector>(frames_fd));
+ std::make_unique<WaylandScreenConnector>(frames_fd), host_mode_ctrl);
} else {
LOG(FATAL) << "Invalid gpu mode: " << config->gpu_mode();
}
@@ -85,15 +95,28 @@
void SetCallback(GenerateProcessedFrameCallback&& frame_callback) {
std::lock_guard<std::mutex> lock(streamer_callback_mutex_);
callback_from_streamer_ = std::move(frame_callback);
- /*
- * the first WaitForAtLeastOneClientConnection() call from VNC requires the
- * Android-frame-processing thread starts beforehands (b/178504150)
- */
- if (!is_frame_fetching_thread_started_) {
- is_frame_fetching_thread_started_ = true;
- sc_android_impl_fetcher_ =
- std::move(std::thread(&ScreenConnector::FrameFetchingLoop, this));
- }
+ streamer_callback_set_cv_.notify_all();
+
+ sc_android_src_->SetFrameCallback(
+ [this](std::uint32_t display_number, std::uint32_t frame_w,
+ std::uint32_t frame_h, std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_bytes) {
+ const bool is_confui_mode = host_mode_ctrl_.IsConfirmatioUiMode();
+ if (is_confui_mode) {
+ return;
+ }
+
+ ProcessedFrameType processed_frame;
+
+ {
+ std::lock_guard<std::mutex> lock(streamer_callback_mutex_);
+ callback_from_streamer_(display_number, frame_w, frame_h,
+ frame_stride_bytes, frame_bytes,
+ processed_frame);
+ }
+
+ sc_frame_multiplexer_.PushToAndroidQueue(std::move(processed_frame));
+ });
}
bool IsCallbackSet() const override {
@@ -108,42 +131,7 @@
*
* NOTE THAT THIS IS THE ONLY CONSUMER OF THE TWO QUEUES
*/
- ProcessedFrameType OnNextFrame() {
- // sc_ctrl has a semaphore internally
- // passing beyond SemWait means either queue has an item
- sc_ctrl_.SemWait();
- return sc_android_queue_.PopFront();
-
- // TODO: add confirmation ui
- /*
- * if (!sc_android_queue_.Empty()) return sc_android_queue_.PopFront();
- * else return conf_ui_queue.PopFront();
- */
- }
-
- [[noreturn]] void FrameFetchingLoop() {
- while (true) {
- sc_ctrl_.WaitAndroidMode( /* pass method to stop sc_android_impl_ */);
- /*
- * TODO: instead of WaitAndroidMode,
- * we could sc_android_impl_->OnFrameAfter but enqueue it only in AndroidMode
- */
- ProcessedFrameType msg;
- decltype(callback_from_streamer_) cp_of_streamer_callback;
- {
- std::lock_guard<std::mutex> lock(streamer_callback_mutex_);
- cp_of_streamer_callback = callback_from_streamer_;
- }
- GenerateProcessedFrameCallbackImpl callback_for_sc_impl =
- std::bind(cp_of_streamer_callback,
- std::placeholders::_1, std::placeholders::_2,
- std::ref(msg));
- bool flag = sc_android_impl_->OnNextFrame(callback_for_sc_impl);
- msg.is_success_ = flag && msg.is_success_;
- auto result = ProcessedFrameType{std::move(msg)};
- sc_android_queue_.PushBack(std::move(result));
- }
- }
+ ProcessedFrameType OnNextFrame() { return sc_frame_multiplexer_.Pop(); }
/**
* ConfUi calls this when it has frames to render
@@ -152,14 +140,34 @@
* Android guest frames if Confirmation UI HAL is not active.
*
*/
- bool RenderConfirmationUi(const std::uint32_t, std::uint8_t*) override {
+ bool RenderConfirmationUi(std::uint32_t display_number,
+ std::uint32_t frame_width,
+ std::uint32_t frame_height,
+ std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_bytes) override {
+ render_confui_cnt_++;
+ // wait callback is not set, the streamer is not ready
+ // return with LOG(ERROR)
+ if (!IsCallbackSet()) {
+ ConfUiLog(ERROR) << "callback function to process frames is not yet set";
+ return false;
+ }
+ ProcessedFrameType processed_frame;
+ auto this_thread_name = cuttlefish::confui::thread::GetName();
+ ConfUiLog(DEBUG) << this_thread_name
+ << "is sending a #" + std::to_string(render_confui_cnt_)
+ << "Conf UI frame";
+ callback_from_streamer_(display_number, frame_width, frame_height,
+ frame_stride_bytes, frame_bytes, processed_frame);
+ // now add processed_frame to the queue
+ sc_frame_multiplexer_.PushToConfUiQueue(std::move(processed_frame));
return true;
}
// Let the screen connector know when there are clients connected
void ReportClientsConnected(bool have_clients) {
// screen connector implementation must implement ReportClientsConnected
- sc_android_impl_->ReportClientsConnected(have_clients);
+ sc_android_src_->ReportClientsConnected(have_clients);
return ;
}
@@ -167,20 +175,30 @@
template <typename T,
typename = std::enable_if_t<
std::is_base_of<ScreenConnectorSource, T>::value, void>>
- ScreenConnector(std::unique_ptr<T>&& impl)
- : sc_android_impl_{std::move(impl)},
- is_frame_fetching_thread_started_(false),
- sc_android_queue_(sc_ctrl_) {}
+ ScreenConnector(std::unique_ptr<T>&& impl, HostModeCtrl& host_mode_ctrl)
+ : sc_android_src_{std::move(impl)},
+ host_mode_ctrl_{host_mode_ctrl},
+ on_next_frame_cnt_{0},
+ render_confui_cnt_{0},
+ sc_frame_multiplexer_{host_mode_ctrl_} {}
ScreenConnector() = delete;
private:
- std::unique_ptr<ScreenConnectorSource> sc_android_impl_; // either socket_based or wayland
- bool is_frame_fetching_thread_started_;
- ScreenConnectorCtrl sc_ctrl_;
- ScreenConnectorQueue<ProcessedFrameType> sc_android_queue_;
+ // either socket_based or wayland
+ std::unique_ptr<ScreenConnectorSource> sc_android_src_;
+ HostModeCtrl& host_mode_ctrl_;
+ unsigned long long int on_next_frame_cnt_;
+ unsigned long long int render_confui_cnt_;
+ /**
+ * internally has conf ui & android queues.
+ *
+ * multiplexting the two input queues, so the consumer gets one input
+ * at a time from the right queue
+ */
+ FrameMultiplexer sc_frame_multiplexer_;
GenerateProcessedFrameCallback callback_from_streamer_;
- std::thread sc_android_impl_fetcher_;
std::mutex streamer_callback_mutex_; // mutex to set & read callback_from_streamer_
+ std::condition_variable streamer_callback_set_cv_;
};
} // namespace cuttlefish
diff --git a/host/libs/screen_connector/screen_connector_common.h b/host/libs/screen_connector/screen_connector_common.h
index 48c7c76..1df6d86 100644
--- a/host/libs/screen_connector/screen_connector_common.h
+++ b/host/libs/screen_connector/screen_connector_common.h
@@ -34,16 +34,20 @@
};
// this callback type is going directly to socket-based or wayland ScreenConnector
-using GenerateProcessedFrameCallbackImpl = std::function<void(std::uint32_t /*display_number*/,
- std::uint8_t* /*frame_pixels*/)>;
+using GenerateProcessedFrameCallbackImpl =
+ std::function<void(std::uint32_t /*display_number*/, //
+ std::uint32_t /*frame_width*/, //
+ std::uint32_t /*frame_height*/, //
+ std::uint32_t /*frame_stride_bytes*/, //
+ std::uint8_t* /*frame_pixels*/)>;
class ScreenConnectorSource {
public:
virtual ~ScreenConnectorSource() = default;
// Runs the given callback on the next available frame after the given
// frame number and returns true if successful.
- virtual bool OnNextFrame(
- const GenerateProcessedFrameCallbackImpl& frame_callback) = 0;
+ virtual void SetFrameCallback(
+ GenerateProcessedFrameCallbackImpl frame_callback) = 0;
virtual void ReportClientsConnected(bool /*have_clients*/) { /* ignore by default */ }
ScreenConnectorSource() = default;
};
@@ -83,8 +87,11 @@
};
struct ScreenConnectorFrameRenderer {
- virtual bool RenderConfirmationUi(const std::uint32_t display,
- std::uint8_t* raw_frame) = 0;
+ virtual bool RenderConfirmationUi(std::uint32_t display_number,
+ std::uint32_t frame_width,
+ std::uint32_t frame_height,
+ std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_bytes) = 0;
virtual bool IsCallbackSet() const = 0;
virtual ~ScreenConnectorFrameRenderer() = default;
};
diff --git a/host/libs/screen_connector/screen_connector_multiplexer.h b/host/libs/screen_connector/screen_connector_multiplexer.h
new file mode 100644
index 0000000..b620531
--- /dev/null
+++ b/host/libs/screen_connector/screen_connector_multiplexer.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 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 <cstdint>
+
+#include "common/libs/concurrency/multiplexer.h"
+#include "common/libs/confui/confui.h"
+
+#include "host/libs/confui/host_mode_ctrl.h"
+#include "host/libs/screen_connector/screen_connector_queue.h"
+
+namespace cuttlefish {
+template <typename ProcessedFrameType>
+class ScreenConnectorInputMultiplexer {
+ using Queue = ScreenConnectorQueue<ProcessedFrameType>;
+ using Multiplexer = Multiplexer<ProcessedFrameType, Queue>;
+
+ public:
+ ScreenConnectorInputMultiplexer(HostModeCtrl& host_mode_ctrl)
+ : host_mode_ctrl_(host_mode_ctrl) {
+ sc_android_queue_id_ =
+ multiplexer_.RegisterQueue(multiplexer_.CreateQueue(/* q size */ 2));
+ sc_confui_queue_id_ =
+ multiplexer_.RegisterQueue(multiplexer_.CreateQueue(/* q size */ 2));
+ }
+
+ virtual ~ScreenConnectorInputMultiplexer() = default;
+
+ void PushToAndroidQueue(ProcessedFrameType&& t) {
+ multiplexer_.Push(sc_android_queue_id_, std::move(t));
+ }
+
+ void PushToConfUiQueue(ProcessedFrameType&& t) {
+ multiplexer_.Push(sc_confui_queue_id_, std::move(t));
+ }
+
+ // customize Pop()
+ ProcessedFrameType Pop() {
+ on_next_frame_cnt_++;
+
+ // is_discard_frame is thread-specific
+ bool is_discard_frame = false;
+
+ // callback to select the queue index, and update is_discard_frame
+ auto selector = [this, &is_discard_frame]() -> int {
+ if (multiplexer_.IsEmpty(sc_android_queue_id_)) {
+ ConfUiLog(VERBOSE)
+ << "Streamer gets Conf UI frame with host ctrl mode = "
+ << static_cast<std::uint32_t>(host_mode_ctrl_.GetMode())
+ << " and cnd = #" << on_next_frame_cnt_;
+ return sc_confui_queue_id_;
+ }
+ auto mode = host_mode_ctrl_.GetMode();
+ if (mode != HostModeCtrl::ModeType::kAndroidMode) {
+ // AndroidFrameFetchingLoop could have added 1 or 2 frames
+ // before it becomes Conf UI mode.
+ ConfUiLog(VERBOSE)
+ << "Streamer ignores Android frame with host ctrl mode ="
+ << static_cast<std::uint32_t>(mode) << "and cnd = #"
+ << on_next_frame_cnt_;
+ is_discard_frame = true;
+ }
+ ConfUiLog(VERBOSE) << "Streamer gets Android frame with host ctrl mode ="
+ << static_cast<std::uint32_t>(mode) << "and cnd = #"
+ << on_next_frame_cnt_;
+ return sc_android_queue_id_;
+ };
+
+ while (true) {
+ ConfUiLog(VERBOSE) << "Streamer waiting Semaphore with host ctrl mode ="
+ << static_cast<std::uint32_t>(
+ host_mode_ctrl_.GetMode())
+ << " and cnd = #" << on_next_frame_cnt_;
+ auto processed_frame = multiplexer_.Pop(selector);
+ if (!is_discard_frame) {
+ return processed_frame;
+ }
+ is_discard_frame = false;
+ }
+ }
+
+ private:
+ HostModeCtrl& host_mode_ctrl_;
+ Multiplexer multiplexer_;
+ unsigned long long int on_next_frame_cnt_;
+ int sc_android_queue_id_;
+ int sc_confui_queue_id_;
+};
+} // end of namespace cuttlefish
diff --git a/host/libs/screen_connector/screen_connector_queue.h b/host/libs/screen_connector/screen_connector_queue.h
index 26c60c9..7643800 100644
--- a/host/libs/screen_connector/screen_connector_queue.h
+++ b/host/libs/screen_connector/screen_connector_queue.h
@@ -16,14 +16,13 @@
#pragma once
+#include <condition_variable>
#include <deque>
#include <memory>
-#include <thread>
#include <mutex>
-#include <condition_variable>
-#include <chrono>
+#include <thread>
-#include "host/libs/screen_connector/screen_connector_ctrl.h"
+#include "common/libs/concurrency/semaphore.h"
namespace cuttlefish {
// move-based concurrent queue
@@ -31,21 +30,17 @@
class ScreenConnectorQueue {
public:
- static const int kQSize = 2;
-
static_assert( is_movable<T>::value,
"Items in ScreenConnectorQueue should be std::mov-able");
- ScreenConnectorQueue(ScreenConnectorCtrl& sc_ctrl_)
- : q_mutex_(std::make_unique<std::mutex>()),
- global_item_tracker_(sc_ctrl_)
- {}
+ ScreenConnectorQueue(const int q_max_size = 2)
+ : q_mutex_(std::make_unique<std::mutex>()), q_max_size_{q_max_size} {}
ScreenConnectorQueue(ScreenConnectorQueue&& cq) = delete;
ScreenConnectorQueue(const ScreenConnectorQueue& cq) = delete;
ScreenConnectorQueue& operator=(const ScreenConnectorQueue& cq) = delete;
ScreenConnectorQueue& operator=(ScreenConnectorQueue&& cq) = delete;
- bool Empty() const {
+ bool IsEmpty() const {
const std::lock_guard<std::mutex> lock(*q_mutex_);
return buffer_.empty();
}
@@ -62,9 +57,9 @@
}
/*
- * PushBack( std::move(src) );
+ * Push( std::move(src) );
*
- * Note: this queue is suppoed to be used only by ScreenConnector-
+ * Note: this queue is supposed to be used only by ScreenConnector-
* related components such as ScreenConnectorSource
*
* The traditional assumption was that when webRTC or VNC calls
@@ -78,7 +73,7 @@
* should stop adding itmes to the queue.
*
*/
- void PushBack(T&& item) {
+ void Push(T&& item) {
std::unique_lock<std::mutex> lock(*q_mutex_);
if (Full()) {
auto is_empty =
@@ -86,23 +81,11 @@
q_empty_.wait(lock, is_empty);
}
buffer_.push_back(std::move(item));
- /* Whether the total number of items in ALL queus is 0 or not
- * is tracked via a semaphore shared by all queues
- *
- * This is NOT intended to block queue from pushing an item
- * This IS intended to awake the screen_connector consumer thread
- * when one or more items are available at least in one queue
- */
- global_item_tracker_.SemPost();
}
- void PushBack(T& item) = delete;
- void PushBack(const T& item) = delete;
+ void Push(T& item) = delete;
+ void Push(const T& item) = delete;
- /*
- * PopFront must be preceded by global_item_tracker_.SemWaitItem()
- *
- */
- T PopFront() {
+ T Pop() {
const std::lock_guard<std::mutex> lock(*q_mutex_);
auto item = std::move(buffer_.front());
buffer_.pop_front();
@@ -116,12 +99,12 @@
bool Full() const {
// call this in a critical section
// after acquiring q_mutex_
- return kQSize == buffer_.size();
+ return q_max_size_ == buffer_.size();
}
std::deque<T> buffer_;
std::unique_ptr<std::mutex> q_mutex_;
std::condition_variable q_empty_;
- ScreenConnectorCtrl& global_item_tracker_;
+ const int q_max_size_;
};
} // namespace cuttlefish
diff --git a/host/libs/screen_connector/wayland_screen_connector.cpp b/host/libs/screen_connector/wayland_screen_connector.cpp
index 7eb6642..dbd4052 100644
--- a/host/libs/screen_connector/wayland_screen_connector.cpp
+++ b/host/libs/screen_connector/wayland_screen_connector.cpp
@@ -33,10 +33,9 @@
server_.reset(new wayland::WaylandServer(wayland_fd));
}
-bool WaylandScreenConnector::OnNextFrame(
- const GenerateProcessedFrameCallbackImpl& frame_callback) {
- server_->OnNextFrame(frame_callback);
- return true;
+void WaylandScreenConnector::SetFrameCallback(
+ GenerateProcessedFrameCallbackImpl frame_callback) {
+ server_->SetFrameCallback(std::move(frame_callback));
}
} // namespace cuttlefish
diff --git a/host/libs/screen_connector/wayland_screen_connector.h b/host/libs/screen_connector/wayland_screen_connector.h
index 00e7ca0..36ed322 100644
--- a/host/libs/screen_connector/wayland_screen_connector.h
+++ b/host/libs/screen_connector/wayland_screen_connector.h
@@ -28,8 +28,8 @@
public:
WaylandScreenConnector(int frames_fd);
- bool OnNextFrame(
- const GenerateProcessedFrameCallbackImpl& frame_callback) override;
+ void SetFrameCallback(
+ GenerateProcessedFrameCallbackImpl frame_callback) override;
private:
std::unique_ptr<wayland::WaylandServer> server_;
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index 4f136fd..b441977 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -40,9 +40,10 @@
namespace {
-std::string GetControlSocketPath(const CuttlefishConfig& config) {
- return config.ForDefaultInstance()
- .PerInstanceInternalPath("crosvm_control.sock");
+std::string GetControlSocketPath(const CuttlefishConfig& config,
+ const std::string& socket_name) {
+ return config.ForDefaultInstance().PerInstanceInternalPath(
+ socket_name.c_str());
}
SharedFD AddTapFdParameter(Command* crosvm_cmd,
@@ -77,11 +78,11 @@
return success;
}
-bool Stop() {
+bool Stop(const std::string& socket_name) {
auto config = CuttlefishConfig::Get();
Command command(config->crosvm_binary());
command.AddParameter("stop");
- command.AddParameter(GetControlSocketPath(*config));
+ command.AddParameter(GetControlSocketPath(*config, socket_name));
auto process = command.Start();
@@ -106,7 +107,7 @@
// HALs.
if (gpu_mode == kGpuModeGuestSwiftshader) {
return {
- "androidboot.cpuvulkan.version=" + std::to_string(VK_API_VERSION_1_1),
+ "androidboot.cpuvulkan.version=" + std::to_string(VK_API_VERSION_1_2),
"androidboot.hardware.gralloc=minigbm",
"androidboot.hardware.hwcomposer=ranchu",
"androidboot.hardware.egl=angle",
@@ -126,7 +127,7 @@
return {
"androidboot.cpuvulkan.version=0",
"androidboot.hardware.gralloc=minigbm",
- "androidboot.hardware.hwcomposer=drm_minigbm",
+ "androidboot.hardware.hwcomposer=ranchu",
"androidboot.hardware.egl=emulation",
"androidboot.hardware.vulkan=ranchu",
"androidboot.hardware.gltransport=virtio-gpu-asg",
@@ -147,18 +148,35 @@
}
}
+constexpr auto crosvm_socket = "crosvm_control.sock";
+constexpr auto crosvm_for_ap_socket = "crosvm_for_ap_control.sock";
+
std::vector<Command> CrosvmManager::StartCommands(
const CuttlefishConfig& config) {
auto instance = config.ForDefaultInstance();
Command crosvm_cmd(config.crosvm_binary(), [](Subprocess* proc) {
- auto stopped = Stop();
+ auto stopped = Stop(crosvm_socket);
if (stopped) {
- return true;
+ return StopperResult::kStopSuccess;
}
LOG(WARNING) << "Failed to stop VMM nicely, attempting to KILL";
- return KillSubprocess(proc);
+ return KillSubprocess(proc) == StopperResult::kStopSuccess
+ ? StopperResult::kStopCrash
+ : StopperResult::kStopFailure;
});
+ bool use_ap_instance =
+ !config.ap_rootfs_image().empty() && !config.ap_kernel_image().empty();
+ Command ap_cmd(config.crosvm_binary(), [](Subprocess* proc) {
+ auto stopped = Stop(crosvm_for_ap_socket);
+ if (stopped) {
+ return StopperResult::kStopSuccess;
+ }
+ LOG(WARNING) << "Failed to stop VMM for AP nicely, attempting to KILL";
+ return KillSubprocess(proc) == StopperResult::kStopSuccess
+ ? StopperResult::kStopCrash
+ : StopperResult::kStopFailure;
+ });
int hvc_num = 0;
int serial_num = 0;
auto add_hvc_sink = [&crosvm_cmd, &hvc_num]() {
@@ -202,6 +220,7 @@
};
crosvm_cmd.AddParameter("run");
+ ap_cmd.AddParameter("run");
if (!config.smt()) {
crosvm_cmd.AddParameter("--no-smt");
@@ -211,6 +230,13 @@
crosvm_cmd.AddParameter("--vhost-net");
}
+ if (!config.vhost_user_mac80211_hwsim().empty()) {
+ crosvm_cmd.AddParameter("--vhost-user-mac80211-hwsim=",
+ config.vhost_user_mac80211_hwsim());
+ ap_cmd.AddParameter("--vhost-user-mac80211-hwsim=",
+ config.vhost_user_mac80211_hwsim());
+ }
+
if (config.protected_vm()) {
crosvm_cmd.AddParameter("--protected-vm");
}
@@ -220,23 +246,20 @@
crosvm_cmd.AddParameter("--gdb=", config.gdb_port());
}
- auto display_configs = config.display_configs();
- CHECK_GE(display_configs.size(), 1);
- auto display_config = display_configs[0];
-
auto gpu_mode = config.gpu_mode();
-
if (gpu_mode == kGpuModeGuestSwiftshader) {
- crosvm_cmd.AddParameter("--gpu=2D,",
- "width=", display_config.width, ",",
- "height=", display_config.height);
+ crosvm_cmd.AddParameter("--gpu=2D");
} else if (gpu_mode == kGpuModeDrmVirgl || gpu_mode == kGpuModeGfxStream) {
crosvm_cmd.AddParameter(gpu_mode == kGpuModeGfxStream ?
"--gpu=gfxstream," : "--gpu=",
- "width=", display_config.width, ",",
- "height=", display_config.height, ",",
"egl=true,surfaceless=true,glx=false,gles=true");
}
+
+ for (const auto& display_config : config.display_configs()) {
+ crosvm_cmd.AddParameter("--gpu-display=", "width=", display_config.width,
+ ",", "height=", display_config.height);
+ }
+
crosvm_cmd.AddParameter("--wayland-sock=", instance.frames_socket_path());
// crosvm_cmd.AddParameter("--null-audio");
@@ -251,23 +274,40 @@
crosvm_cmd.AddParameter(config.protected_vm() ? "--disk=" :
"--rwdisk=", disk);
}
- crosvm_cmd.AddParameter("--socket=", GetControlSocketPath(config));
+ crosvm_cmd.AddParameter("--socket=",
+ GetControlSocketPath(config, crosvm_socket));
+ ap_cmd.AddParameter("--socket=",
+ GetControlSocketPath(config, crosvm_for_ap_socket));
if (config.enable_vnc_server() || config.enable_webrtc()) {
auto touch_type_parameter =
config.enable_webrtc() ? "--multi-touch=" : "--single-touch=";
- crosvm_cmd.AddParameter(touch_type_parameter, instance.touch_socket_path(),
- ":", display_config.width, ":",
- display_config.height);
+
+ auto display_configs = config.display_configs();
+ CHECK_GE(display_configs.size(), 1);
+
+ for (int i = 0; i < display_configs.size(); ++i) {
+ auto display_config = display_configs[i];
+
+ crosvm_cmd.AddParameter(touch_type_parameter,
+ instance.touch_socket_path(i), ":",
+ display_config.width, ":", display_config.height);
+ }
crosvm_cmd.AddParameter("--keyboard=", instance.keyboard_socket_path());
}
if (config.enable_webrtc()) {
crosvm_cmd.AddParameter("--switches=", instance.switches_socket_path());
}
- auto wifi_tap = AddTapFdParameter(&crosvm_cmd, instance.wifi_tap_name());
AddTapFdParameter(&crosvm_cmd, instance.mobile_tap_name());
+ SharedFD wifi_tap;
+ if (config.vhost_user_mac80211_hwsim().empty()) {
+ wifi_tap = AddTapFdParameter(&crosvm_cmd, instance.wifi_tap_name());
+ } else if (use_ap_instance) {
+ wifi_tap = AddTapFdParameter(&ap_cmd, instance.wifi_tap_name());
+ }
+
if (FileExists(instance.access_kregistry_path())) {
crosvm_cmd.AddParameter("--rw-pmem-device=",
instance.access_kregistry_path());
@@ -289,8 +329,10 @@
return {};
}
crosvm_cmd.AddParameter("--seccomp-policy-dir=", config.seccomp_policy_dir());
+ ap_cmd.AddParameter("--seccomp-policy-dir=", config.seccomp_policy_dir());
} else {
crosvm_cmd.AddParameter("--disable-sandbox");
+ ap_cmd.AddParameter("--disable-sandbox");
}
if (instance.vsock_guest_cid() >= 2) {
@@ -376,7 +418,7 @@
<< VmManager::kMaxDisks + VmManager::kDefaultNumHvcs << " devices";
if (config.enable_audio()) {
- crosvm_cmd.AddParameter("--ac97=backend=vios,capture=false,server=" +
+ crosvm_cmd.AddParameter("--sound=",
config.ForDefaultInstance().audio_server_path());
}
@@ -399,7 +441,8 @@
// Only run the leases workaround if we are not using the new network
// bridge architecture - in that case, we have a wider DHCP address
// space and stale leases should be much less of an issue
- if (!FileExists("/var/run/cuttlefish-dnsmasq-cvd-wbr.leases")) {
+ if (!FileExists("/var/run/cuttlefish-dnsmasq-cvd-wbr.leases") &&
+ (config.vhost_user_mac80211_hwsim().empty() || use_ap_instance)) {
// TODO(schuffelen): QEMU also needs this and this is not the best place for
// this code. Find a better place to put it.
auto lease_file =
@@ -409,9 +452,14 @@
<< "network may not work.";
}
}
+ ap_cmd.AddParameter("--root=", config.ap_rootfs_image());
+ ap_cmd.AddParameter(config.ap_kernel_image());
std::vector<Command> ret;
ret.push_back(std::move(crosvm_cmd));
+ if (use_ap_instance) {
+ ret.push_back(std::move(ap_cmd));
+ }
ret.push_back(std::move(log_tee_cmd));
return ret;
}
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index f26818a..2cd7e46 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -102,7 +102,7 @@
"androidboot.cpuvulkan.version=" + std::to_string(VK_API_VERSION_1_1),
"androidboot.hardware.gralloc=minigbm",
"androidboot.hardware.hwcomposer=ranchu",
- "androidboot.hardware.egl=swiftshader",
+ "androidboot.hardware.egl=angle",
"androidboot.hardware.vulkan=pastel",
};
}
@@ -140,11 +140,13 @@
auto stop = [](Subprocess* proc) {
auto stopped = Stop();
if (stopped) {
- return true;
+ return StopperResult::kStopSuccess;
}
LOG(WARNING) << "Failed to stop VMM nicely, "
<< "attempting to KILL";
- return KillSubprocess(proc);
+ return KillSubprocess(proc) == StopperResult::kStopSuccess
+ ? StopperResult::kStopCrash
+ : StopperResult::kStopFailure;
};
std::string qemu_binary = config.qemu_binary_dir();
switch (arch_) {
@@ -170,7 +172,7 @@
qemu_cmd.AddParameter("null,id=hvc", hvc_num);
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter(
- "virtio-serial-pci-non-transitional,max_ports=1,id=virtio-serial",
+ "virtio-serial-pci,max_ports=1,id=virtio-serial",
hvc_num);
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("virtconsole,bus=virtio-serial", hvc_num,
@@ -207,7 +209,7 @@
",append=on");
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter(
- "virtio-serial-pci-non-transitional,max_ports=1,id=virtio-serial",
+ "virtio-serial-pci,max_ports=1,id=virtio-serial",
hvc_num);
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("virtconsole,bus=virtio-serial", hvc_num,
@@ -219,7 +221,7 @@
qemu_cmd.AddParameter("pipe,id=hvc", hvc_num, ",path=", prefix);
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter(
- "virtio-serial-pci-non-transitional,max_ports=1,id=virtio-serial",
+ "virtio-serial-pci,max_ports=1,id=virtio-serial",
hvc_num);
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("virtconsole,bus=virtio-serial", hvc_num,
@@ -374,7 +376,7 @@
qemu_cmd.AddParameter("file=", disk, ",if=none,id=drive-virtio-disk", i,
",aio=threads", format, readonly);
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-blk-pci-non-transitional,scsi=off,drive=drive-virtio-disk", i,
+ qemu_cmd.AddParameter("virtio-blk-pci,scsi=off,drive=drive-virtio-disk", i,
",id=virtio-disk", i, bootindex);
}
@@ -417,36 +419,45 @@
qemu_cmd.AddParameter("rng-random,id=objrng0,filename=/dev/urandom");
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-rng-pci-non-transitional,rng=objrng0,id=rng0,",
+ qemu_cmd.AddParameter("virtio-rng-pci,rng=objrng0,id=rng0,",
"max-bytes=1024,period=2000");
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-mouse-pci");
+ qemu_cmd.AddParameter("virtio-mouse-pci,disable-legacy=on");
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-keyboard-pci");
+ qemu_cmd.AddParameter("virtio-keyboard-pci,disable-legacy=on");
+
+ // device padding for unsupported "switches" input
+ qemu_cmd.AddParameter("-device");
+ qemu_cmd.AddParameter("virtio-keyboard-pci,disable-legacy=on");
auto vhost_net = config.vhost_net() ? ",vhost=on" : "";
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-balloon-pci-non-transitional,id=balloon0");
+ qemu_cmd.AddParameter("virtio-balloon-pci,id=balloon0");
qemu_cmd.AddParameter("-netdev");
qemu_cmd.AddParameter("tap,id=hostnet0,ifname=", instance.wifi_tap_name(),
",script=no,downscript=no", vhost_net);
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet0,id=net0");
+ qemu_cmd.AddParameter("virtio-net-pci,netdev=hostnet0,id=net0");
qemu_cmd.AddParameter("-netdev");
qemu_cmd.AddParameter("tap,id=hostnet1,ifname=", instance.mobile_tap_name(),
",script=no,downscript=no", vhost_net);
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet1,id=net1");
+ qemu_cmd.AddParameter("virtio-net-pci,netdev=hostnet1,id=net1");
+
+ auto display_configs = config.display_configs();
+ CHECK_GE(display_configs.size(), 1);
+ auto display_config = display_configs[0];
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-gpu-pci,id=gpu0");
+ qemu_cmd.AddParameter("virtio-gpu-pci,id=gpu0,"
+ "xres=", display_config.width, ",yres=", display_config.height);
qemu_cmd.AddParameter("-cpu");
qemu_cmd.AddParameter(IsHostCompatible(arch_) ? "host" : "max");
@@ -455,7 +466,7 @@
qemu_cmd.AddParameter("timestamp=on");
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("vhost-vsock-pci-non-transitional,guest-cid=",
+ qemu_cmd.AddParameter("vhost-vsock-pci,guest-cid=",
instance.vsock_guest_cid());
qemu_cmd.AddParameter("-device");
@@ -469,7 +480,7 @@
",script=no,downscript=no", vhost_net);
qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet2,id=net2");
+ qemu_cmd.AddParameter("virtio-net-pci,netdev=hostnet2,id=net2");
}
qemu_cmd.AddParameter("-device");
diff --git a/host/libs/wayland/Android.bp b/host/libs/wayland/Android.bp
index 4be6528..12700f3 100644
--- a/host/libs/wayland/Android.bp
+++ b/host/libs/wayland/Android.bp
@@ -28,6 +28,7 @@
"wayland_subcompositor.cpp",
"wayland_surface.cpp",
"wayland_surfaces.cpp",
+ "wayland_virtio_gpu_metadata.cpp",
],
shared_libs: [
"libbase",
@@ -37,6 +38,7 @@
static_libs: [
"libdrm",
"libffi",
+ "libwayland_crosvm_gpu_display_extension_server_protocols",
"libwayland_server",
"libwayland_extension_server_protocols",
],
diff --git a/host/libs/wayland/wayland_compositor.cpp b/host/libs/wayland/wayland_compositor.cpp
index 6179092..4e2f8f5 100644
--- a/host/libs/wayland/wayland_compositor.cpp
+++ b/host/libs/wayland/wayland_compositor.cpp
@@ -182,7 +182,10 @@
.damage_buffer = surface_damage_buffer,
};
-void surface_destroy_resource_callback(struct wl_resource*) {}
+void surface_destroy_resource_callback(struct wl_resource* surface_resource) {
+ Surface* surface = GetUserData<Surface>(surface_resource);
+ delete surface;
+}
void compositor_create_surface(wl_client* client,
wl_resource* compositor,
@@ -191,12 +194,8 @@
<< " compositor=" << compositor
<< " id=" << id;
- // Wayland seems to use a single global id space for all objects.
- static std::atomic<std::uint32_t> sNextDisplayId{0};
- uint32_t display_id = sNextDisplayId++;
-
Surfaces* surfaces = GetUserData<Surfaces>(compositor);
- Surface* surface = surfaces->GetOrCreateSurface(display_id);
+ Surface* surface = new Surface(*surfaces);
wl_resource* surface_resource = wl_resource_create(
client, &wl_surface_interface, wl_resource_get_version(compositor), id);
diff --git a/host/libs/wayland/wayland_server.cpp b/host/libs/wayland/wayland_server.cpp
index 4dc4b88..7dbc7f7 100644
--- a/host/libs/wayland/wayland_server.cpp
+++ b/host/libs/wayland/wayland_server.cpp
@@ -28,6 +28,7 @@
#include "host/libs/wayland/wayland_subcompositor.h"
#include "host/libs/wayland/wayland_surface.h"
#include "host/libs/wayland/wayland_utils.h"
+#include "host/libs/wayland/wayland_virtio_gpu_metadata.h"
namespace wayland {
namespace internal {
@@ -79,6 +80,8 @@
wl_display_init_shm(server_state_->display_);
BindCompositorInterface(server_state_->display_, &server_state_->surfaces_);
+ BindVirtioGpuMetadataInterface(server_state_->display_,
+ &server_state_->surfaces_);
BindDmabufInterface(server_state_->display_);
BindSubcompositorInterface(server_state_->display_);
BindSeatInterface(server_state_->display_);
@@ -94,8 +97,8 @@
wl_display_destroy(server_state_->display_);
}
-void WaylandServer::OnNextFrame(const Surfaces::FrameCallback& callback) {
- server_state_->surfaces_.OnNextFrame(callback);
+void WaylandServer::SetFrameCallback(Surfaces::FrameCallback callback) {
+ server_state_->surfaces_.SetFrameCallback(std::move(callback));
}
} // namespace wayland
diff --git a/host/libs/wayland/wayland_server.h b/host/libs/wayland/wayland_server.h
index 3149481..24ee68c 100644
--- a/host/libs/wayland/wayland_server.h
+++ b/host/libs/wayland/wayland_server.h
@@ -48,8 +48,9 @@
WaylandServer(WaylandServer&& rhs) = delete;
WaylandServer& operator=(WaylandServer&& rhs) = delete;
- // Blocks until the given callback is run on the next frame available.
- void OnNextFrame(const Surfaces::FrameCallback& callback);
+ // Registers the callback that will be run whenever a new frame is
+ // available.
+ void SetFrameCallback(Surfaces::FrameCallback callback);
private:
void ServerLoop(int wayland_socket_fd);
diff --git a/host/libs/wayland/wayland_shell.cpp b/host/libs/wayland/wayland_shell.cpp
index 439fc62..424c878 100644
--- a/host/libs/wayland/wayland_shell.cpp
+++ b/host/libs/wayland/wayland_shell.cpp
@@ -20,35 +20,29 @@
#include <wayland-server-core.h>
#include <wayland-server-protocol.h>
-#include <xdg-shell-unstable-v6-server-protocol.h>
+#include <xdg-shell-server-protocol.h>
namespace wayland {
namespace {
-
-void zxdg_positioner_v6_destroy(wl_client*, wl_resource* positioner) {
+void xdg_positioner_destroy(wl_client*, wl_resource* positioner) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner;
wl_resource_destroy(positioner);
}
-void zxdg_positioner_v6_set_size(wl_client*,
- wl_resource* positioner,
- int32_t w,
- int32_t h) {
+void xdg_positioner_set_size(wl_client*, wl_resource* positioner, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " w=" << w
<< " h=" << h;
}
-void zxdg_positioner_v6_set_anchor_rect(wl_client*,
- wl_resource* positioner,
- int32_t x,
- int32_t y,
- int32_t w,
- int32_t h) {
+void xdg_positioner_set_anchor_rect(wl_client*, wl_resource* positioner,
+ int32_t x, int32_t y, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " x=" << x
@@ -57,87 +51,76 @@
<< " h=" << h;
}
-void zxdg_positioner_v6_set_anchor(wl_client*,
- wl_resource* positioner,
- uint32_t anchor) {
+void xdg_positioner_set_anchor(wl_client*, wl_resource* positioner,
+ uint32_t anchor) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " anchor=" << anchor;
}
-void zxdg_positioner_v6_set_gravity(wl_client*,
- wl_resource* positioner,
- uint32_t gravity) {
+void xdg_positioner_set_gravity(wl_client*, wl_resource* positioner,
+ uint32_t gravity) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " gravity=" << gravity;
}
-void zxdg_positioner_v6_set_constraint_adjustment(wl_client*,
- wl_resource* positioner,
- uint32_t adjustment) {
+void xdg_positioner_set_constraint_adjustment(wl_client*,
+ wl_resource* positioner,
+ uint32_t adjustment) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " adjustment=" << adjustment;
}
-void zxdg_positioner_v6_set_offset(wl_client*,
- wl_resource* positioner,
- int32_t x,
- int32_t y) {
+void xdg_positioner_set_offset(wl_client*, wl_resource* positioner, int32_t x,
+ int32_t y) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " x=" << x
<< " y=" << y;
}
-const struct zxdg_positioner_v6_interface
- zxdg_positioner_v6_implementation = {
- .destroy = zxdg_positioner_v6_destroy,
- .set_size = zxdg_positioner_v6_set_size,
- .set_anchor_rect = zxdg_positioner_v6_set_anchor_rect,
- .set_anchor = zxdg_positioner_v6_set_anchor,
- .set_gravity = zxdg_positioner_v6_set_gravity,
- .set_constraint_adjustment = zxdg_positioner_v6_set_constraint_adjustment,
- .set_offset = zxdg_positioner_v6_set_offset};
+const struct xdg_positioner_interface xdg_positioner_implementation = {
+ .destroy = xdg_positioner_destroy,
+ .set_size = xdg_positioner_set_size,
+ .set_anchor_rect = xdg_positioner_set_anchor_rect,
+ .set_anchor = xdg_positioner_set_anchor,
+ .set_gravity = xdg_positioner_set_gravity,
+ .set_constraint_adjustment = xdg_positioner_set_constraint_adjustment,
+ .set_offset = xdg_positioner_set_offset};
-void zxdg_toplevel_v6_destroy(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_destroy(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
wl_resource_destroy(toplevel);
}
-void zxdg_toplevel_v6_set_parent(wl_client*,
- wl_resource* toplevel,
- wl_resource* parent_toplevel) {
+void xdg_toplevel_set_parent(wl_client*, wl_resource* toplevel,
+ wl_resource* parent_toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " parent_toplevel=" << parent_toplevel;
}
-void zxdg_toplevel_v6_set_title(wl_client*,
- wl_resource* toplevel,
- const char* title) {
+void xdg_toplevel_set_title(wl_client*, wl_resource* toplevel,
+ const char* title) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " title=" << title;
}
-void zxdg_toplevel_v6_set_app_id(wl_client*,
- wl_resource* toplevel,
- const char* app) {
+void xdg_toplevel_set_app_id(wl_client*, wl_resource* toplevel,
+ const char* app) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " app=" << app;
}
-void zxdg_toplevel_v6_show_window_menu(wl_client*,
- wl_resource* toplevel,
- wl_resource* seat,
- uint32_t serial,
- int32_t x,
- int32_t y) {
+void xdg_toplevel_show_window_menu(wl_client*, wl_resource* toplevel,
+ wl_resource* seat, uint32_t serial,
+ int32_t x, int32_t y) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " seat=" << seat
@@ -146,21 +129,16 @@
<< " y=" << y;
}
-void zxdg_toplevel_v6_move(wl_client*,
- wl_resource* toplevel,
- wl_resource* seat,
- uint32_t serial) {
+void xdg_toplevel_move(wl_client*, wl_resource* toplevel, wl_resource* seat,
+ uint32_t serial) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " seat=" << seat
<< " serial=" << serial;
}
-void zxdg_toplevel_v6_resize(wl_client*,
- wl_resource* toplevel,
- wl_resource* seat,
- uint32_t serial,
- uint32_t edges) {
+void xdg_toplevel_resize(wl_client*, wl_resource* toplevel, wl_resource* seat,
+ uint32_t serial, uint32_t edges) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " seat=" << seat
@@ -168,93 +146,83 @@
<< " edges=" << edges;
}
-void zxdg_toplevel_v6_set_max_size(wl_client*,
- wl_resource* toplevel,
- int32_t w,
- int32_t h) {
+void xdg_toplevel_set_max_size(wl_client*, wl_resource* toplevel, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " w=" << w
<< " h=" << h;
}
-void zxdg_toplevel_v6_set_min_size(wl_client*,
- wl_resource* toplevel,
- int32_t w,
- int32_t h) {
+void xdg_toplevel_set_min_size(wl_client*, wl_resource* toplevel, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " w=" << w
<< " h=" << h;
}
-void zxdg_toplevel_v6_set_maximized(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_set_maximized(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-void zxdg_toplevel_v6_unset_maximized(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_unset_maximized(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-void zxdg_toplevel_v6_set_fullscreen(wl_client*,
- wl_resource* toplevel,
- wl_resource*) {
+void xdg_toplevel_set_fullscreen(wl_client*, wl_resource* toplevel,
+ wl_resource*) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-void zxdg_toplevel_v6_unset_fullscreen(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_unset_fullscreen(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-void zxdg_toplevel_v6_set_minimized(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_set_minimized(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation = {
- .destroy = zxdg_toplevel_v6_destroy,
- .set_parent = zxdg_toplevel_v6_set_parent,
- .set_title = zxdg_toplevel_v6_set_title,
- .set_app_id = zxdg_toplevel_v6_set_app_id,
- .show_window_menu = zxdg_toplevel_v6_show_window_menu,
- .move = zxdg_toplevel_v6_move,
- .resize = zxdg_toplevel_v6_resize,
- .set_max_size = zxdg_toplevel_v6_set_max_size,
- .set_min_size = zxdg_toplevel_v6_set_min_size,
- .set_maximized = zxdg_toplevel_v6_set_maximized,
- .unset_maximized = zxdg_toplevel_v6_unset_maximized,
- .set_fullscreen = zxdg_toplevel_v6_set_fullscreen,
- .unset_fullscreen = zxdg_toplevel_v6_unset_fullscreen,
- .set_minimized = zxdg_toplevel_v6_set_minimized
-};
+const struct xdg_toplevel_interface xdg_toplevel_implementation = {
+ .destroy = xdg_toplevel_destroy,
+ .set_parent = xdg_toplevel_set_parent,
+ .set_title = xdg_toplevel_set_title,
+ .set_app_id = xdg_toplevel_set_app_id,
+ .show_window_menu = xdg_toplevel_show_window_menu,
+ .move = xdg_toplevel_move,
+ .resize = xdg_toplevel_resize,
+ .set_max_size = xdg_toplevel_set_max_size,
+ .set_min_size = xdg_toplevel_set_min_size,
+ .set_maximized = xdg_toplevel_set_maximized,
+ .unset_maximized = xdg_toplevel_unset_maximized,
+ .set_fullscreen = xdg_toplevel_set_fullscreen,
+ .unset_fullscreen = xdg_toplevel_unset_fullscreen,
+ .set_minimized = xdg_toplevel_set_minimized};
-void zxdg_popup_v6_destroy(wl_client*, wl_resource* popup) {
+void xdg_popup_destroy(wl_client*, wl_resource* popup) {
LOG(VERBOSE) << __FUNCTION__
<< " popup=" << popup;
wl_resource_destroy(popup);
}
-void zxdg_popup_v6_grab(wl_client*,
- wl_resource* popup,
- wl_resource* seat,
- uint32_t serial) {
+void xdg_popup_grab(wl_client*, wl_resource* popup, wl_resource* seat,
+ uint32_t serial) {
LOG(VERBOSE) << __FUNCTION__
<< " popup=" << popup
<< " seat=" << seat
<< " serial=" << serial;
}
-const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation = {
- .destroy = zxdg_popup_v6_destroy,
- .grab = zxdg_popup_v6_grab
-};
+const struct xdg_popup_interface xdg_popup_implementation = {
+ .destroy = xdg_popup_destroy, .grab = xdg_popup_grab};
-void zxdg_surface_v6_destroy(wl_client*, wl_resource* surface) {
+void xdg_surface_destroy(wl_client*, wl_resource* surface) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface;
@@ -263,28 +231,25 @@
void toplevel_destroy_resource_callback(struct wl_resource*) {}
-void zxdg_surface_v6_get_toplevel(wl_client* client,
- wl_resource* surface,
- uint32_t id) {
+void xdg_surface_get_toplevel(wl_client* client, wl_resource* surface,
+ uint32_t id) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface
<< " id=" << id;
wl_resource* xdg_toplevel_resource =
- wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
+ wl_resource_create(client, &xdg_toplevel_interface, 1, id);
wl_resource_set_implementation(xdg_toplevel_resource,
- &zxdg_toplevel_v6_implementation, nullptr,
+ &xdg_toplevel_implementation, nullptr,
toplevel_destroy_resource_callback);
}
void popup_destroy_resource_callback(struct wl_resource*) {}
-void zxdg_surface_v6_get_popup(wl_client* client,
- wl_resource* surface,
- uint32_t id,
- wl_resource* parent_surface,
- wl_resource* positioner) {
+void xdg_surface_get_popup(wl_client* client, wl_resource* surface, uint32_t id,
+ wl_resource* parent_surface,
+ wl_resource* positioner) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface
<< " id=" << id
@@ -292,19 +257,15 @@
<< " positioner=" << positioner;
wl_resource* xdg_popup_resource =
- wl_resource_create(client, &zxdg_popup_v6_interface, 1, id);
+ wl_resource_create(client, &xdg_popup_interface, 1, id);
- wl_resource_set_implementation(xdg_popup_resource,
- &zxdg_popup_v6_implementation, nullptr,
- popup_destroy_resource_callback);
+ wl_resource_set_implementation(xdg_popup_resource, &xdg_popup_implementation,
+ nullptr, popup_destroy_resource_callback);
}
-void zxdg_surface_v6_set_window_geometry(wl_client*,
- wl_resource* surface,
- int32_t x,
- int32_t y,
- int32_t w,
- int32_t h) {
+void xdg_surface_set_window_geometry(wl_client*, wl_resource* surface,
+ int32_t x, int32_t y, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface
<< " x=" << x
@@ -313,23 +274,21 @@
<< " h=" << h;
}
-void zxdg_surface_v6_ack_configure(wl_client*,
- wl_resource* surface,
- uint32_t serial) {
+void xdg_surface_ack_configure(wl_client*, wl_resource* surface,
+ uint32_t serial) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface
<< " serial=" << serial;
}
-const struct zxdg_surface_v6_interface zxdg_surface_v6_implementation = {
- .destroy = zxdg_surface_v6_destroy,
- .get_toplevel = zxdg_surface_v6_get_toplevel,
- .get_popup = zxdg_surface_v6_get_popup,
- .set_window_geometry = zxdg_surface_v6_set_window_geometry,
- .ack_configure = zxdg_surface_v6_ack_configure
-};
+const struct xdg_surface_interface xdg_surface_implementation = {
+ .destroy = xdg_surface_destroy,
+ .get_toplevel = xdg_surface_get_toplevel,
+ .get_popup = xdg_surface_get_popup,
+ .set_window_geometry = xdg_surface_set_window_geometry,
+ .ack_configure = xdg_surface_ack_configure};
-void zxdg_shell_v6_destroy(wl_client*, wl_resource* shell) {
+void xdg_shell_destroy(wl_client*, wl_resource* shell) {
LOG(VERBOSE) << __FUNCTION__
<< " shell=" << shell;
@@ -338,65 +297,60 @@
void positioner_destroy_resource_callback(struct wl_resource*) {}
-void zxdg_shell_v6_create_positioner(wl_client* client,
- wl_resource* shell,
- uint32_t id) {
+void xdg_shell_create_positioner(wl_client* client, wl_resource* shell,
+ uint32_t id) {
LOG(VERBOSE) << __FUNCTION__
<< " shell=" << shell
<< " id=" << id;
wl_resource* positioner_resource =
- wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id);
+ wl_resource_create(client, &xdg_positioner_interface, 1, id);
wl_resource_set_implementation(positioner_resource,
- &zxdg_positioner_v6_implementation, nullptr,
+ &xdg_positioner_implementation, nullptr,
positioner_destroy_resource_callback);
}
void surface_destroy_resource_callback(struct wl_resource*) {}
-void zxdg_shell_v6_get_xdg_surface(wl_client* client,
- wl_resource* shell,
- uint32_t id,
- wl_resource* surface) {
+void xdg_shell_get_xdg_surface(wl_client* client, wl_resource* shell,
+ uint32_t id, wl_resource* surface) {
LOG(VERBOSE) << __FUNCTION__
<< " shell=" << shell
<< " id=" << id
<< " surface=" << surface;
wl_resource* surface_resource =
- wl_resource_create(client, &zxdg_surface_v6_interface, 1, id);
+ wl_resource_create(client, &xdg_surface_interface, 1, id);
- wl_resource_set_implementation(surface_resource,
- &zxdg_surface_v6_implementation, nullptr,
- surface_destroy_resource_callback);
+ wl_resource_set_implementation(surface_resource, &xdg_surface_implementation,
+ nullptr, surface_destroy_resource_callback);
}
-void zxdg_shell_v6_pong(wl_client*, wl_resource* shell, uint32_t serial) {
+void xdg_shell_pong(wl_client*, wl_resource* shell, uint32_t serial) {
LOG(VERBOSE) << __FUNCTION__
<< " shell=" << shell
<< " serial=" << serial;
}
-const struct zxdg_shell_v6_interface zxdg_shell_v6_implementation = {
- .destroy = zxdg_shell_v6_destroy,
- .create_positioner = zxdg_shell_v6_create_positioner,
- .get_xdg_surface = zxdg_shell_v6_get_xdg_surface,
- .pong = zxdg_shell_v6_pong
-};
+const struct xdg_wm_base_interface xdg_shell_implementation = {
+ .destroy = xdg_shell_destroy,
+ .create_positioner = xdg_shell_create_positioner,
+ .get_xdg_surface = xdg_shell_get_xdg_surface,
+ .pong = xdg_shell_pong};
void bind_shell(wl_client* client, void* data, uint32_t version, uint32_t id) {
wl_resource* shell_resource =
- wl_resource_create(client, &zxdg_shell_v6_interface, version, id);
+ wl_resource_create(client, &xdg_wm_base_interface, version, id);
- wl_resource_set_implementation(shell_resource,
- &zxdg_shell_v6_implementation, data, nullptr);
+ wl_resource_set_implementation(shell_resource, &xdg_shell_implementation,
+ data, nullptr);
}
} // namespace
void BindShellInterface(wl_display* display) {
- wl_global_create(display, &zxdg_shell_v6_interface, 1, nullptr, bind_shell);
+ wl_global_create(display, &xdg_wm_base_interface, 1, nullptr, bind_shell);
}
} // namespace wayland
\ No newline at end of file
diff --git a/host/libs/wayland/wayland_surface.cpp b/host/libs/wayland/wayland_surface.cpp
index e9abece..51a3b42 100644
--- a/host/libs/wayland/wayland_surface.cpp
+++ b/host/libs/wayland/wayland_surface.cpp
@@ -23,8 +23,7 @@
namespace wayland {
-Surface::Surface(std::uint32_t display_number, Surfaces& surfaces)
- : display_number_(display_number), surfaces_(surfaces) {}
+Surface::Surface(Surfaces& surfaces) : surfaces_(surfaces) {}
void Surface::SetRegion(const Region& region) {
std::unique_lock<std::mutex> lock(state_mutex_);
@@ -45,22 +44,28 @@
return;
}
- struct wl_shm_buffer* shm_buffer = wl_shm_buffer_get(state_.current_buffer);
- CHECK(shm_buffer != nullptr);
+ if (state_.virtio_gpu_metadata_.scanout_id.has_value()) {
+ const uint32_t display_number = *state_.virtio_gpu_metadata_.scanout_id;
- wl_shm_buffer_begin_access(shm_buffer);
+ struct wl_shm_buffer* shm_buffer = wl_shm_buffer_get(state_.current_buffer);
+ CHECK(shm_buffer != nullptr);
- const int32_t buffer_w = wl_shm_buffer_get_width(shm_buffer);
- CHECK(buffer_w == state_.region.w);
- const int32_t buffer_h = wl_shm_buffer_get_height(shm_buffer);
- CHECK(buffer_h == state_.region.h);
+ wl_shm_buffer_begin_access(shm_buffer);
- uint8_t* buffer_pixels =
- reinterpret_cast<uint8_t*>(wl_shm_buffer_get_data(shm_buffer));
+ const int32_t buffer_w = wl_shm_buffer_get_width(shm_buffer);
+ CHECK(buffer_w == state_.region.w);
+ const int32_t buffer_h = wl_shm_buffer_get_height(shm_buffer);
+ CHECK(buffer_h == state_.region.h);
+ const int32_t buffer_stride_bytes = wl_shm_buffer_get_stride(shm_buffer);
- surfaces_.HandleSurfaceFrame(display_number_, buffer_pixels);
+ uint8_t* buffer_pixels =
+ reinterpret_cast<uint8_t*>(wl_shm_buffer_get_data(shm_buffer));
- wl_shm_buffer_end_access(shm_buffer);
+ surfaces_.HandleSurfaceFrame(display_number, buffer_w, buffer_h,
+ buffer_stride_bytes, buffer_pixels);
+
+ wl_shm_buffer_end_access(shm_buffer);
+ }
wl_buffer_send_release(state_.current_buffer);
wl_client_flush(wl_resource_get_client(state_.current_buffer));
@@ -69,4 +74,9 @@
state_.current_frame_number++;
}
+void Surface::SetVirtioGpuScanoutId(uint32_t scanout_id) {
+ std::unique_lock<std::mutex> lock(state_mutex_);
+ state_.virtio_gpu_metadata_.scanout_id = scanout_id;
+}
+
} // namespace wayland
\ No newline at end of file
diff --git a/host/libs/wayland/wayland_surface.h b/host/libs/wayland/wayland_surface.h
index fcf2345..f3b17ae 100644
--- a/host/libs/wayland/wayland_surface.h
+++ b/host/libs/wayland/wayland_surface.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <mutex>
+#include <optional>
#include <wayland-server-core.h>
@@ -29,7 +30,7 @@
// Tracks the buffer associated with a Wayland surface.
class Surface {
public:
- Surface(std::uint32_t display_number, Surfaces& surfaces);
+ Surface(Surfaces& surfaces);
virtual ~Surface() = default;
Surface(const Surface& rhs) = delete;
@@ -53,10 +54,15 @@
// Commits the pending frame state.
void Commit();
+ void SetVirtioGpuScanoutId(uint32_t scanout);
+
private:
- std::uint32_t display_number_;
Surfaces& surfaces_;
+ struct VirtioGpuMetadata {
+ std::optional<uint32_t> scanout_id;
+ };
+
struct State {
uint32_t current_frame_number = 0;
@@ -68,6 +74,8 @@
// The buffers expected dimensions.
Region region;
+
+ VirtioGpuMetadata virtio_gpu_metadata_;
};
std::mutex state_mutex_;
diff --git a/host/libs/wayland/wayland_surfaces.cpp b/host/libs/wayland/wayland_surfaces.cpp
index 7096574..2fa1ed9 100644
--- a/host/libs/wayland/wayland_surfaces.cpp
+++ b/host/libs/wayland/wayland_surfaces.cpp
@@ -23,42 +23,20 @@
namespace wayland {
-Surface* Surfaces::GetOrCreateSurface(std::uint32_t id) {
- std::unique_lock<std::mutex> lock(surfaces_mutex_);
-
- auto [it, inserted] = surfaces_.try_emplace(id, nullptr);
-
- std::unique_ptr<Surface>& surface_ptr = it->second;
- if (inserted) {
- surface_ptr.reset(new Surface(id, *this));
- }
- return surface_ptr.get();
-}
-
-void Surfaces::OnNextFrame(const FrameCallback& frame_callback) {
- // Wraps the given callback in a std::package_task that can be waited upon
- // for completion.
- Surfaces::FrameCallbackPackaged frame_callback_packaged(
- [&frame_callback](std::uint32_t display_number,
- std::uint8_t* frame_pixels) {
- frame_callback(display_number, frame_pixels);
- });
-
- {
- std::unique_lock<std::mutex> lock(callback_mutex_);
- callback_.emplace(&frame_callback_packaged);
- }
-
- // Blocks until the frame_callback_packaged was called.
- frame_callback_packaged.get_future().get();
+void Surfaces::SetFrameCallback(FrameCallback callback) {
+ std::unique_lock<std::mutex> lock(callback_mutex_);
+ callback_.emplace(std::move(callback));
}
void Surfaces::HandleSurfaceFrame(std::uint32_t display_number,
+ std::uint32_t frame_width,
+ std::uint32_t frame_height,
+ std::uint32_t frame_stride_bytes,
std::uint8_t* frame_bytes) {
std::unique_lock<std::mutex> lock(callback_mutex_);
if (callback_) {
- (*callback_.value())(display_number, frame_bytes);
- callback_.reset();
+ (callback_.value())(display_number, frame_width, frame_height,
+ frame_stride_bytes, frame_bytes);
}
}
diff --git a/host/libs/wayland/wayland_surfaces.h b/host/libs/wayland/wayland_surfaces.h
index ec67ca4..58ed0e8 100644
--- a/host/libs/wayland/wayland_surfaces.h
+++ b/host/libs/wayland/wayland_surfaces.h
@@ -39,27 +39,25 @@
Surfaces(Surfaces&& rhs) = delete;
Surfaces& operator=(Surfaces&& rhs) = delete;
- Surface* GetOrCreateSurface(std::uint32_t id);
+ using FrameCallback =
+ std::function<void(std::uint32_t /*display_number*/, //
+ std::uint32_t /*frame_width*/, //
+ std::uint32_t /*frame_height*/, //
+ std::uint32_t /*frame_stride_bytes*/, //
+ std::uint8_t* /*frame_bytes*/)>;
- using FrameCallback = std::function<void(std::uint32_t /*display_number*/,
- std::uint8_t* /*frame_pixels*/)>;
-
- // Blocking
- void OnNextFrame(const FrameCallback& callback);
+ void SetFrameCallback(FrameCallback callback);
private:
friend class Surface;
- void HandleSurfaceFrame(std::uint32_t display_number,
+ void HandleSurfaceFrame(std::uint32_t display_number, //
+ std::uint32_t frame_width, //
+ std::uint32_t frame_height, //
+ std::uint32_t frame_stride_bytes, //
std::uint8_t* frame_bytes);
- std::mutex surfaces_mutex_;
- std::unordered_map<std::uint32_t, std::unique_ptr<Surface>> surfaces_;
-
- using FrameCallbackPackaged = std::packaged_task<void(
- std::uint32_t /*display_number*/, std::uint8_t* /*frame_bytes*/)>;
-
std::mutex callback_mutex_;
- std::optional<FrameCallbackPackaged*> callback_;
+ std::optional<FrameCallback> callback_;
};
} // namespace wayland
diff --git a/host/libs/wayland/wayland_virtio_gpu_metadata.cpp b/host/libs/wayland/wayland_virtio_gpu_metadata.cpp
new file mode 100644
index 0000000..e26c653
--- /dev/null
+++ b/host/libs/wayland/wayland_virtio_gpu_metadata.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 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/wayland/wayland_compositor.h"
+
+#include <android-base/logging.h>
+
+#include <virtio-gpu-metadata-v1.h>
+#include <wayland-server-core.h>
+#include <wayland-server-protocol.h>
+
+#include "host/libs/wayland/wayland_surface.h"
+#include "host/libs/wayland/wayland_utils.h"
+
+namespace wayland {
+namespace {
+
+void virtio_gpu_surface_metadata_set_scanout_id(
+ struct wl_client*, struct wl_resource* surface_metadata_resource,
+ uint32_t scanout_id) {
+ GetUserData<Surface>(surface_metadata_resource)
+ ->SetVirtioGpuScanoutId(scanout_id);
+}
+
+const struct wp_virtio_gpu_surface_metadata_v1_interface
+ virtio_gpu_surface_metadata_implementation = {
+ .set_scanout_id = virtio_gpu_surface_metadata_set_scanout_id};
+
+void destroy_virtio_gpu_surface_metadata_resource_callback(
+ struct wl_resource*) {
+ // This is only expected to occur upon surface destruction so no need to
+ // update the scanout id in `Surface`.
+}
+
+void virtio_gpu_metadata_get_surface_metadata(
+ struct wl_client* client, struct wl_resource* /*metadata_impl_resource*/,
+ uint32_t id, struct wl_resource* surface_resource) {
+ Surface* surface = GetUserData<Surface>(surface_resource);
+
+ wl_resource* virtio_gpu_metadata_surface_resource = wl_resource_create(
+ client, &wp_virtio_gpu_surface_metadata_v1_interface, 1, id);
+
+ wl_resource_set_implementation(
+ virtio_gpu_metadata_surface_resource,
+ &virtio_gpu_surface_metadata_implementation, surface,
+ destroy_virtio_gpu_surface_metadata_resource_callback);
+}
+
+const struct wp_virtio_gpu_metadata_v1_interface
+ virtio_gpu_metadata_implementation = {
+ .get_surface_metadata = virtio_gpu_metadata_get_surface_metadata,
+};
+
+void destroy_virtio_gpu_metadata_resource_callback(struct wl_resource*) {}
+
+void bind_virtio_gpu_metadata(wl_client* client, void* data,
+ uint32_t /*version*/, uint32_t id) {
+ wl_resource* resource =
+ wl_resource_create(client, &wp_virtio_gpu_metadata_v1_interface, 1, id);
+
+ wl_resource_set_implementation(resource, &virtio_gpu_metadata_implementation,
+ data,
+ destroy_virtio_gpu_metadata_resource_callback);
+}
+
+} // namespace
+
+void BindVirtioGpuMetadataInterface(wl_display* display, Surfaces* surfaces) {
+ wl_global_create(display, &wp_virtio_gpu_metadata_v1_interface, 1, surfaces,
+ bind_virtio_gpu_metadata);
+}
+
+} // namespace wayland
\ No newline at end of file
diff --git a/common/libs/utils/size_utils.cpp b/host/libs/wayland/wayland_virtio_gpu_metadata.h
similarity index 60%
copy from common/libs/utils/size_utils.cpp
copy to host/libs/wayland/wayland_virtio_gpu_metadata.h
index 9f25445..c414c84 100644
--- a/common/libs/utils/size_utils.cpp
+++ b/host/libs/wayland/wayland_virtio_gpu_metadata.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 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,15 +14,17 @@
* limitations under the License.
*/
-#include "common/libs/utils/size_utils.h"
+#pragma once
-#include <unistd.h>
+#include <stdint.h>
-namespace cuttlefish {
+#include <wayland-server-core.h>
-uint64_t AlignToPowerOf2(uint64_t val, uint8_t align_log) {
- uint64_t align = 1ULL << align_log;
- return ((val + (align - 1)) / align) * align;
-}
+#include "host/libs/wayland/wayland_surfaces.h"
-} // namespace cuttlefish
+namespace wayland {
+
+// Binds the virtio gpu metadata interface to the given wayland server.
+void BindVirtioGpuMetadataInterface(wl_display* display, Surfaces* surfaces);
+
+} // namespace wayland
\ No newline at end of file
diff --git a/host/libs/websocket/Android.bp b/host/libs/websocket/Android.bp
index 2a4a522..327224b 100644
--- a/host/libs/websocket/Android.bp
+++ b/host/libs/websocket/Android.bp
@@ -28,6 +28,7 @@
"liblog",
"libssl",
"libcrypto",
+ "libcuttlefish_utils",
],
static_libs: [
"libcap",
diff --git a/host/libs/websocket/websocket_handler.cpp b/host/libs/websocket/websocket_handler.cpp
index 59b1e1e..4d93ebd 100644
--- a/host/libs/websocket/websocket_handler.cpp
+++ b/host/libs/websocket/websocket_handler.cpp
@@ -18,9 +18,17 @@
#include <android-base/logging.h>
#include <libwebsockets.h>
+#include "host/libs/websocket/websocket_server.h"
+
namespace cuttlefish {
-const size_t WebSocketHandler::WsBuffer::kLwsPre = LWS_PRE;
+namespace {
+void AppendData(const char* data, size_t len, std::string& buffer) {
+ auto ptr = reinterpret_cast<const uint8_t*>(data);
+ buffer.reserve(buffer.size() + len);
+ buffer.insert(buffer.end(), ptr, ptr + len);
+}
+} // namespace
WebSocketHandler::WebSocketHandler(struct lws* wsi) : wsi_(wsi) {}
@@ -34,32 +42,29 @@
// Attempts to write what's left on a websocket buffer to the websocket,
// updating the buffer.
-// Returns true if the entire buffer was successfully written.
-bool WebSocketHandler::WriteWsBuffer(WebSocketHandler::WsBuffer& ws_buffer) {
- auto len = ws_buffer.data.size() - ws_buffer.start;
+void WebSocketHandler::WriteWsBuffer(WebSocketHandler::WsBuffer& ws_buffer) {
+ auto len = ws_buffer.data.size() - LWS_PRE;
auto flags = lws_write_ws_flags(
ws_buffer.binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT, true, true);
- auto res = lws_write(wsi_, &ws_buffer.data[ws_buffer.start], len,
+ auto res = lws_write(wsi_, &ws_buffer.data[LWS_PRE], len,
static_cast<enum lws_write_protocol>(flags));
+ // lws_write will write all bytes of the provided buffer or enqueue the ones
+ // it couldn't write for later, but it guarantees it will consume the entire
+ // buffer, so we only need to check for error.
if (res < 0) {
// This shouldn't happen since this function is called in response to a
// LWS_CALLBACK_SERVER_WRITEABLE call.
- LOG(FATAL) << "Failed to write data on the websocket";
- // Close
- return true;
+ LOG(ERROR) << "Failed to write data on the websocket";
}
- ws_buffer.start += res;
- return ws_buffer.start == ws_buffer.data.size();
}
bool WebSocketHandler::OnWritable() {
if (buffer_queue_.empty()) {
return close_;
}
- auto wrote_full_buffer = WriteWsBuffer(buffer_queue_.back());
- if (wrote_full_buffer) {
- buffer_queue_.pop_back();
- }
+ WriteWsBuffer(buffer_queue_.back());
+ buffer_queue_.pop_back();
+
if (!buffer_queue_.empty()) {
lws_callback_on_writable(wsi_);
}
@@ -72,4 +77,24 @@
lws_callback_on_writable(wsi_);
}
+DynHandler::DynHandler(struct lws* wsi) : wsi_(wsi), out_buffer_(LWS_PRE, 0) {}
+
+void DynHandler::AppendDataOut(const std::string& data) {
+ AppendData(data.c_str(), data.size(), out_buffer_);
+}
+
+void DynHandler::AppendDataIn(void* data, size_t len) {
+ AppendData(reinterpret_cast<char*>(data), len, in_buffer_);
+}
+
+void DynHandler::OnWritable() {
+ auto len = out_buffer_.size() - LWS_PRE;
+ auto res = lws_write(wsi_, reinterpret_cast<uint8_t*>(&out_buffer_[LWS_PRE]),
+ len, LWS_WRITE_HTTP_FINAL);
+ if (res != len) {
+ // This shouldn't happen since this function is called in response to a
+ // LWS_CALLBACK_SERVER_WRITEABLE call.
+ LOG(ERROR) << "Failed to write HTTP response";
+ }
+}
} // namespace cuttlefish
diff --git a/host/libs/websocket/websocket_handler.h b/host/libs/websocket/websocket_handler.h
index 0111b1e..572ef76 100644
--- a/host/libs/websocket/websocket_handler.h
+++ b/host/libs/websocket/websocket_handler.h
@@ -16,6 +16,7 @@
#pragma once
#include <deque>
+#include <string>
#include <vector>
struct lws;
@@ -28,6 +29,10 @@
virtual ~WebSocketHandler() = default;
virtual void OnReceive(const uint8_t* msg, size_t len, bool binary) = 0;
+ virtual void OnReceive(const uint8_t* msg, size_t len, bool binary,
+ [[maybe_unused]] bool is_final) {
+ OnReceive(msg, len, binary);
+ }
virtual void OnConnected() = 0;
virtual void OnClosed() = 0;
@@ -40,15 +45,13 @@
private:
struct WsBuffer {
- static const size_t kLwsPre;
WsBuffer(std::vector<uint8_t> data, bool binary)
: data(std::move(data)), binary(binary) {}
std::vector<uint8_t> data;
bool binary;
- size_t start = kLwsPre;
};
- bool WriteWsBuffer(WsBuffer& ws_buffer);
+ void WriteWsBuffer(WsBuffer& ws_buffer);
struct lws* wsi_;
bool close_ = false;
@@ -61,4 +64,39 @@
virtual std::shared_ptr<WebSocketHandler> Build(struct lws* wsi) = 0;
};
+class WebSocketServer;
+
+class DynHandler {
+ public:
+ DynHandler(struct lws* wsi);
+
+ virtual ~DynHandler() = default;
+ // TODO (jemoreira): Allow more than just JSON replies
+ // TODO (jemoreira): Receive request parameters
+ // Handle a GET request. Returns the status code of the request.
+ virtual int DoGet() = 0;
+ // Handle a POST request. Returns the status code of the request.
+ virtual int DoPost() = 0;
+
+ protected:
+ void AppendDataOut(const std::string& data);
+ const std::string& GetDataIn() const { return in_buffer_; }
+
+ private:
+ friend WebSocketServer;
+ void AppendDataIn(void* data, size_t len);
+ void OnWritable();
+
+ struct lws* wsi_;
+ std::string in_buffer_ = {};
+ std::string out_buffer_ = {};
+};
+
+class DynHandlerFactory {
+ public:
+ virtual ~DynHandlerFactory() = default;
+ // A new Handler will be created for each connection
+ virtual std::unique_ptr<DynHandler> Build(struct lws* wsi) = 0;
+};
+
} // namespace cuttlefish
diff --git a/host/libs/websocket/websocket_server.cpp b/host/libs/websocket/websocket_server.cpp
index 75110f8..2b87ab6 100644
--- a/host/libs/websocket/websocket_server.cpp
+++ b/host/libs/websocket/websocket_server.cpp
@@ -22,31 +22,136 @@
#include <android-base/logging.h>
#include <libwebsockets.h>
+#include <common/libs/utils/files.h>
#include <host/libs/websocket/websocket_handler.h>
namespace cuttlefish {
-WebSocketServer::WebSocketServer(
- const char* protocol_name,
- const std::string &certs_dir,
- const std::string &assets_dir,
- int server_port) {
- std::string cert_file = certs_dir + "/server.crt";
- std::string key_file = certs_dir + "/server.key";
+namespace {
+
+std::string GetPath(struct lws* wsi) {
+ auto len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
+ std::string path(len + 1, '\0');
+ auto ret = lws_hdr_copy(wsi, path.data(), path.size(), WSI_TOKEN_GET_URI);
+ if (ret <= 0) {
+ len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH);
+ path.resize(len + 1, '\0');
+ ret =
+ lws_hdr_copy(wsi, path.data(), path.size(), WSI_TOKEN_HTTP_COLON_PATH);
+ }
+ if (ret < 0) {
+ LOG(FATAL) << "Something went wrong getting the path";
+ }
+ path.resize(len);
+ return path;
+}
+
+bool WriteCommonHttpHeaders(int status, const char* mime_type,
+ struct lws* wsi) {
+ constexpr size_t BUFF_SIZE = 2048;
+ uint8_t header_buffer[LWS_PRE + BUFF_SIZE];
+ const auto start = &header_buffer[LWS_PRE];
+ auto p = &header_buffer[LWS_PRE];
+ auto end = start + BUFF_SIZE;
+ if (lws_add_http_common_headers(wsi, status, mime_type,
+ LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end)) {
+ LOG(ERROR) << "Failed to write headers for response";
+ return false;
+ }
+ if (lws_finalize_write_http_header(wsi, start, &p, end)) {
+ LOG(ERROR) << "Failed to finalize headers for response";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+WebSocketServer::WebSocketServer(const char* protocol_name,
+ const std::string& assets_dir, int server_port)
+ : WebSocketServer(protocol_name, "", assets_dir, server_port) {}
+
+WebSocketServer::WebSocketServer(const char* protocol_name,
+ const std::string& certs_dir,
+ const std::string& assets_dir, int server_port)
+ : protocol_name_(protocol_name),
+ assets_dir_(assets_dir),
+ certs_dir_(certs_dir),
+ server_port_(server_port) {}
+
+void WebSocketServer::InitializeLwsObjects() {
+ std::string cert_file = certs_dir_ + "/server.crt";
+ std::string key_file = certs_dir_ + "/server.key";
+ std::string ca_file = certs_dir_ + "/CA.crt";
retry_ = {
.secs_since_valid_ping = 3,
.secs_since_valid_hangup = 10,
};
- struct lws_protocols protocols[] = {
- {protocol_name, ServerCallback, 4096, 0, 0, nullptr, 0},
- {nullptr, nullptr, 0, 0, 0, nullptr, 0}};
+ struct lws_protocols protocols[] = //
+ {{
+ .name = protocol_name_.c_str(),
+ .callback = WebsocketCallback,
+ .per_session_data_size = 0,
+ .rx_buffer_size = 0,
+ .id = 0,
+ .user = this,
+ .tx_packet_size = 0,
+ },
+ {
+ .name = "__http_polling__",
+ .callback = DynHttpCallback,
+ .per_session_data_size = 0,
+ .rx_buffer_size = 0,
+ .id = 0,
+ .user = this,
+ .tx_packet_size = 0,
+ },
+ {
+ .name = nullptr,
+ .callback = nullptr,
+ .per_session_data_size = 0,
+ .rx_buffer_size = 0,
+ .id = 0,
+ .user = nullptr,
+ .tx_packet_size = 0,
+ }};
- mount_ = {
- .mount_next = nullptr,
+ dyn_mounts_.reserve(dyn_handler_factories_.size());
+ for (auto& handler_entry : dyn_handler_factories_) {
+ auto& path = handler_entry.first;
+ dyn_mounts_.push_back({
+ .mount_next = nullptr,
+ .mountpoint = path.c_str(),
+ .mountpoint_len = static_cast<uint8_t>(path.size()),
+ .origin = "__http_polling__",
+ .def = nullptr,
+ .protocol = nullptr,
+ .cgienv = nullptr,
+ .extra_mimetypes = nullptr,
+ .interpret = nullptr,
+ .cgi_timeout = 0,
+ .cache_max_age = 0,
+ .auth_mask = 0,
+ .cache_reusable = 0,
+ .cache_revalidate = 0,
+ .cache_intermediaries = 0,
+ .origin_protocol = LWSMPRO_CALLBACK, // dynamic
+ .basic_auth_login_file = nullptr,
+ });
+ }
+ struct lws_http_mount* next_mount = nullptr;
+ // Set up the linked list after all the mounts have been created to ensure
+ // pointers are not invalidated.
+ for (auto& mount : dyn_mounts_) {
+ mount.mount_next = next_mount;
+ next_mount = &mount;
+ }
+
+ static_mount_ = {
+ .mount_next = next_mount,
.mountpoint = "/",
.mountpoint_len = 1,
- .origin = assets_dir.c_str(),
+ .origin = assets_dir_.c_str(),
.def = "index.html",
.protocol = nullptr,
.cgienv = nullptr,
@@ -63,24 +168,29 @@
};
struct lws_context_creation_info info;
- headers_ = {NULL, NULL,
- "content-security-policy:",
- "default-src 'self'; "
- "style-src 'self' https://fonts.googleapis.com/; "
- "font-src https://fonts.gstatic.com/; "};
+ headers_ = {NULL, NULL, "content-security-policy:",
+ "default-src 'self' https://ajax.googleapis.com; "
+ "style-src 'self' https://fonts.googleapis.com/; "
+ "font-src https://fonts.gstatic.com/; "};
memset(&info, 0, sizeof info);
- info.port = server_port;
- info.mounts = &mount_;
+ info.port = server_port_;
+ info.mounts = &static_mount_;
info.protocols = protocols;
info.vhost_name = "localhost";
info.ws_ping_pong_interval = 10;
info.headers = &headers_;
- info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
- info.ssl_cert_filepath = cert_file.c_str();
- info.ssl_private_key_filepath = key_file.c_str();
info.retry_and_idle_policy = &retry_;
+ if (!certs_dir_.empty()) {
+ info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+ info.ssl_cert_filepath = cert_file.c_str();
+ info.ssl_private_key_filepath = key_file.c_str();
+ if (FileExists(ca_file)) {
+ info.ssl_ca_filepath = ca_file.c_str();
+ }
+ }
+
context_ = lws_create_context(&info);
if (!context_) {
LOG(FATAL) << "Failed to create websocket context";
@@ -88,12 +198,19 @@
}
void WebSocketServer::RegisterHandlerFactory(
- const std::string &path,
+ const std::string& path,
std::unique_ptr<WebSocketHandlerFactory> handler_factory_p) {
handler_factories_[path] = std::move(handler_factory_p);
}
+void WebSocketServer::RegisterDynHandlerFactory(
+ const std::string& path,
+ std::unique_ptr<DynHandlerFactory> handler_factory_p) {
+ dyn_handler_factories_[path] = std::move(handler_factory_p);
+}
+
void WebSocketServer::Serve() {
+ InitializeLwsObjects();
int n = 0;
while (n >= 0) {
n = lws_service(context_, 0);
@@ -101,22 +218,104 @@
lws_context_destroy(context_);
}
-std::unordered_map<struct lws*, std::shared_ptr<WebSocketHandler>> WebSocketServer::handlers_ = {};
-std::unordered_map<std::string, std::unique_ptr<WebSocketHandlerFactory>>
- WebSocketServer::handler_factories_ = {};
-
-std::string WebSocketServer::GetPath(struct lws* wsi) {
- auto len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
- std::string path(len + 1, '\0');
- auto ret = lws_hdr_copy(wsi, path.data(), path.size(), WSI_TOKEN_GET_URI);
- if (ret < 0) {
- LOG(FATAL) << "Something went wrong getting the path";
+int WebSocketServer::WebsocketCallback(struct lws* wsi,
+ enum lws_callback_reasons reason,
+ void* user, void* in, size_t len) {
+ auto protocol = lws_get_protocol(wsi);
+ if (!protocol) {
+ // Some callback reasons are always handled by the first protocol, before a
+ // wsi struct is even created.
+ return lws_callback_http_dummy(wsi, reason, user, in, len);
}
- path.resize(len);
- return path;
+ return reinterpret_cast<WebSocketServer*>(protocol->user)
+ ->ServerCallback(wsi, reason, user, in, len);
}
-int WebSocketServer::ServerCallback(struct lws* wsi, enum lws_callback_reasons reason,
+int WebSocketServer::DynHttpCallback(struct lws* wsi,
+ enum lws_callback_reasons reason,
+ void* user, void* in, size_t len) {
+ auto protocol = lws_get_protocol(wsi);
+ if (!protocol) {
+ LOG(ERROR) << "No protocol associated with connection";
+ return 1;
+ }
+ return reinterpret_cast<WebSocketServer*>(protocol->user)
+ ->DynServerCallback(wsi, reason, user, in, len);
+}
+
+int WebSocketServer::DynServerCallback(struct lws* wsi,
+ enum lws_callback_reasons reason,
+ void* user, void* in, size_t len) {
+ switch (reason) {
+ case LWS_CALLBACK_HTTP: {
+ char* path_raw;
+ int path_len;
+ auto method = lws_http_get_uri_and_method(wsi, &path_raw, &path_len);
+ if (method < 0) {
+ return 1;
+ }
+ std::string path(path_raw, path_len);
+ auto handler = InstantiateDynHandler(path, wsi);
+ dyn_handlers_[wsi] = std::move(handler);
+ switch (method) {
+ case LWSHUMETH_GET: {
+ auto status = dyn_handlers_[wsi]->DoGet();
+ if (!WriteCommonHttpHeaders(status, "application/json", wsi)) {
+ return 1;
+ }
+ // Write the response later, when the server is ready
+ lws_callback_on_writable(wsi);
+ break;
+ }
+ case LWSHUMETH_POST:
+ // Do nothing until the body has been read
+ break;
+ default:
+ LOG(ERROR) << "Unsupported HTTP method: " << method;
+ return 1;
+ }
+ break;
+ }
+ case LWS_CALLBACK_HTTP_BODY: {
+ auto handler = dyn_handlers_[wsi].get();
+ if (!handler) {
+ LOG(WARNING) << "Received body for unknown wsi";
+ return 1;
+ }
+ handler->AppendDataIn(in, len);
+ break;
+ }
+ case LWS_CALLBACK_HTTP_BODY_COMPLETION: {
+ auto handler = dyn_handlers_[wsi].get();
+ auto status = handler->DoPost();
+ if (!WriteCommonHttpHeaders(status, "application/json", wsi)) {
+ return 1;
+ }
+ lws_callback_on_writable(wsi);
+ break;
+ }
+ case LWS_CALLBACK_HTTP_WRITEABLE: {
+ auto handler = dyn_handlers_[wsi].get();
+ if (!handler) {
+ LOG(WARNING) << "Unknown wsi became writable";
+ return 1;
+ }
+ handler->OnWritable();
+ // Make sure the connection (in HTTP 1) or stream (in HTTP 2) is closed
+ // after the response is written
+ return 1;
+ }
+ case LWS_CALLBACK_CLOSED_HTTP:
+ dyn_handlers_.erase(wsi);
+ break;
+ default:
+ return lws_callback_http_dummy(wsi, reason, user, in, len);
+ }
+ return 0;
+}
+
+int WebSocketServer::ServerCallback(struct lws* wsi,
+ enum lws_callback_reasons reason,
void* user, void* in, size_t len) {
switch (reason) {
case LWS_CALLBACK_ESTABLISHED: {
@@ -156,8 +355,10 @@
case LWS_CALLBACK_RECEIVE: {
auto handler = handlers_[wsi];
if (handler) {
+ bool is_final = (lws_remaining_packet_payload(wsi) == 0) &&
+ lws_is_final_fragment(wsi);
handler->OnReceive(reinterpret_cast<const uint8_t*>(in), len,
- lws_frame_is_binary(wsi));
+ lws_frame_is_binary(wsi), is_final);
} else {
LOG(WARNING) << "Unkwnown wsi sent data";
}
@@ -181,4 +382,16 @@
}
}
+std::unique_ptr<DynHandler> WebSocketServer::InstantiateDynHandler(
+ const std::string& uri_path, struct lws* wsi) {
+ auto it = dyn_handler_factories_.find(uri_path);
+ if (it == dyn_handler_factories_.end()) {
+ LOG(ERROR) << "Wrong path provided in URI: " << uri_path;
+ return nullptr;
+ } else {
+ LOG(INFO) << "Creating handler for " << uri_path;
+ return it->second->Build(wsi);
+ }
+}
+
} // namespace cuttlefish
diff --git a/host/libs/websocket/websocket_server.h b/host/libs/websocket/websocket_server.h
index 0695b3b..877a9c7 100644
--- a/host/libs/websocket/websocket_server.h
+++ b/host/libs/websocket/websocket_server.h
@@ -18,6 +18,7 @@
#include <string>
#include <unordered_map>
+#include <vector>
#include <android-base/logging.h>
#include <libwebsockets.h>
@@ -27,32 +28,63 @@
namespace cuttlefish {
class WebSocketServer {
public:
- WebSocketServer(
- const char* protocol_name,
- const std::string &certs_dir,
- const std::string &assets_dir,
- int port);
+ // Uses HTTP and WS
+ WebSocketServer(const char* protocol_name, const std::string& assets_dir,
+ int port);
+ // Uses HTTPS and WSS when a certificates directory is provided
+ WebSocketServer(const char* protocol_name, const std::string& certs_dir,
+ const std::string& assets_dir, int port);
~WebSocketServer() = default;
+ // Register a handler factory for websocket connections. A new handler will be
+ // created for each new websocket connection.
void RegisterHandlerFactory(
- const std::string &path,
- std::unique_ptr<WebSocketHandlerFactory> handler_factory_p);
+ const std::string& path,
+ std::unique_ptr<WebSocketHandlerFactory> handler_factory_p);
+
+ // Register a handler factory for dynamic HTTP requests. A new handler will be
+ // created for each HTTP request.
+ void RegisterDynHandlerFactory(
+ const std::string& path,
+ std::unique_ptr<DynHandlerFactory> handler_factory_p);
+
void Serve();
-
private:
- static std::unordered_map<struct lws*, std::shared_ptr<WebSocketHandler>> handlers_;
- static std::unordered_map<std::string, std::unique_ptr<WebSocketHandlerFactory>>
- handler_factories_;
+ static int WebsocketCallback(struct lws* wsi,
+ enum lws_callback_reasons reason, void* user,
+ void* in, size_t len);
- static std::string GetPath(struct lws* wsi);
- static int ServerCallback(struct lws* wsi, enum lws_callback_reasons reason,
+ static int DynHttpCallback(struct lws* wsi, enum lws_callback_reasons reason,
+ void* user, void* in, size_t len);
+
+ int ServerCallback(struct lws* wsi, enum lws_callback_reasons reason,
void* user, void* in, size_t len);
- static std::shared_ptr<WebSocketHandler> InstantiateHandler(
+ int DynServerCallback(struct lws* wsi,
+ enum lws_callback_reasons reason, void* user,
+ void* in, size_t len);
+ std::shared_ptr<WebSocketHandler> InstantiateHandler(
+ const std::string& uri_path, struct lws* wsi);
+ std::unique_ptr<DynHandler> InstantiateDynHandler(
const std::string& uri_path, struct lws* wsi);
+ void InitializeLwsObjects();
+
+ std::unordered_map<struct lws*, std::shared_ptr<WebSocketHandler>> handlers_ =
+ {};
+ std::unordered_map<std::string, std::unique_ptr<WebSocketHandlerFactory>>
+ handler_factories_ = {};
+ std::unordered_map<struct lws*, std::unique_ptr<DynHandler>> dyn_handlers_ =
+ {};
+ std::unordered_map<std::string, std::unique_ptr<DynHandlerFactory>>
+ dyn_handler_factories_ = {};
+ std::string protocol_name_;
+ std::string assets_dir_;
+ std::string certs_dir_;
+ int server_port_;
struct lws_context* context_;
- struct lws_http_mount mount_;
+ struct lws_http_mount static_mount_;
+ std::vector<struct lws_http_mount> dyn_mounts_ = {};
struct lws_protocol_vhost_options headers_;
lws_retry_bo_t retry_;
};
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index 7820cac..ab0b5a8 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -144,9 +144,16 @@
USE_OPENGL_RENDERER := true
# Wifi.
+ifeq ($(PRODUCT_ENFORCE_MAC80211_HWSIM),true)
+BOARD_WLAN_DEVICE := emulator
+BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated_cf
+WIFI_HIDL_FEATURE_DUAL_INTERFACE := true
+else
BOARD_WLAN_DEVICE := wlan0
+endif
BOARD_HOSTAPD_DRIVER := NL80211
BOARD_WPA_SUPPLICANT_DRIVER := NL80211
+BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated_cf
WPA_SUPPLICANT_VERSION := VER_0_8_X
WIFI_DRIVER_FW_PATH_PARAM := "/dev/null"
WIFI_DRIVER_FW_PATH_STA := "/dev/null"
@@ -159,7 +166,7 @@
BOARD_SEPOLICY_DIRS += system/bt/vendor_libs/linux/sepolicy
# Avoid multiple includes of sepolicy already included by Pixel experience.
-ifneq ($(filter aosp_% %_auto %_tv,$(PRODUCT_NAME)),)
+ifneq ($(filter aosp_% %_auto %_go_phone trout_% %_tv,$(PRODUCT_NAME)),)
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS += hardware/google/pixel-sepolicy/flipendo
@@ -209,7 +216,7 @@
BOARD_KERNEL_CMDLINE += firmware_class.path=/vendor/etc/
BOARD_KERNEL_CMDLINE += init=/init
-BOARD_BOOTCONFIG += hardware=cutf_cvm
+BOARD_BOOTCONFIG += androidboot.hardware=cutf_cvm
# TODO(b/179489292): Remove once kfence is enabled everywhere
BOARD_KERNEL_CMDLINE += kfence.sample_interval=500
@@ -219,8 +226,6 @@
# TODO(b/182417593): Move all of these module options to modules.options
# TODO(b/176860479): Remove once goldfish and cuttlefish share a wifi implementation
BOARD_BOOTCONFIG += kernel.mac80211_hwsim.radios=0
-# TODO(b/175151042): Remove once we are using virtio-snd on cuttlefish
-BOARD_BOOTCONFIG += kernel.snd-hda-intel.enable=0
# Reduce slab size usage from virtio vsock to reduce slab fragmentation
BOARD_BOOTCONFIG += \
kernel.vmw_vsock_virtio_transport_common.virtio_transport_max_vsock_pkt_buf_size=16384
@@ -253,7 +258,6 @@
BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT := true
endif
BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT := true
-BOARD_KERNEL_MODULE_INTERFACE_VERSIONS := 5.10-android12-0
BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD := dm-user.ko
diff --git a/shared/config/grub.cfg b/shared/config/grub.cfg
index 958c6a3..d15eb8e 100644
--- a/shared/config/grub.cfg
+++ b/shared/config/grub.cfg
@@ -4,7 +4,7 @@
# These options are accessible to chain-loaded configurations as well:
#
# pnpacpi=off Disable on QEMU; allows serdev to claim platform serial
-# pci=noacpi Crosvm doesn't support ACPI-based PCI enumeration
+# acpi=noirq Do not configure IRQ routing using ACPI tables
# reboot=k Reboot using keyboard method, rather than ACPI
# noexec=off Some kernels panic when setting up NX
# noefi Some kernels panic when trying to use U-Boot EFI
@@ -13,7 +13,7 @@
# console=ttyAMA0 QEMU on ARM64 uses alternative serial implementation
#
if [ "$grub_cpu" = "i386" ]; then
- set cmdline="pnpacpi=off pci=noacpi reboot=k noexec=off console=ttyS0 noefi panic=-1 console=hvc0 snd-hda-intel.enable=0"
+ set cmdline="pnpacpi=off acpi=noirq reboot=k noexec=off console=ttyS0 noefi panic=-1 console=hvc0"
elif [ "$grub_cpu" = "arm64" ]; then
set cmdline="console=ttyS0 console=ttyAMA0 noefi panic=-1 console=hvc0"
else
@@ -21,7 +21,7 @@
fi
# Root filesystem is on a GUID partition with label "otheros_root"
-set rootfs="PARTLABEL=otheros_root"
+set rootfs="/dev/vda14"
# Root filesystem with grub installed
search --file --set root /boot/grub/grub.cfg --hint (hd0)
diff --git a/shared/config/init.insmod.sh b/shared/config/init.insmod.sh
deleted file mode 100755
index ccbf716..0000000
--- a/shared/config/init.insmod.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/vendor/bin/sh
-
-# 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.
-#
-
-KERNEL_VERSION_NUMBER=`uname -r`
-MAINLINE_STR='mainline'
-if [[ $KERNEL_VERSION_NUMBER == *$MAINLINE_STR* ]]; then
- IS_MAINLINE=1
-else
- IS_MAINLINE=0
-fi
-
-KERNEL_VERSION_NUMBER=`echo $KERNEL_VERSION_NUMBER | grep -o -E '^[0-9]+\.[0-9]+'`
-# This folder on cuttlefish contains modules for multiple kernel versions.
-# Hence the need to filter them instead of relying on module.order
-VENDOR_MODULES='/vendor/lib/modules/*.ko'
-
-for f in $VENDOR_MODULES
-do
- MOD_VERSION=`modinfo $f`
- MOD_VERSION=`echo $MOD_VERSION | grep -o -E 'vermagic: [0-9a-zA-Z\.-]+'`
- MOD_VERSION_NUMBER=`echo $MOD_VERSION | grep -o -E '[0-9]+\.[0-9]+'`
- if [[ $MOD_VERSION == *$MAINLINE_STR* ]]; then
- IS_MOD_MAINLINE=1
- else
- IS_MOD_MAINLINE=0
- fi
-
- # TODO (137683279) When we have a few more kernel modules, we'll have to do the module
- # insertion of least dependencies.
- if [ $IS_MOD_MAINLINE -eq $IS_MAINLINE ] && [ $MOD_VERSION_NUMBER == $KERNEL_VERSION_NUMBER ]
- then
- `insmod $f`
- echo "Insmod " $f
- fi
-done
diff --git a/shared/config/init.recovery.rc b/shared/config/init.recovery.rc
index 6f9ca141..3fdd4b0 100644
--- a/shared/config/init.recovery.rc
+++ b/shared/config/init.recovery.rc
@@ -3,8 +3,8 @@
console
disabled
user root
- group shell log readproc
- seclabel u:r:shell:s0
+ group root shell log readproc
+ seclabel u:r:su:s0
setenv HOSTNAME console
on property:ro.debuggable=1
diff --git a/shared/config/init.vendor.rc b/shared/config/init.vendor.rc
index 184833f..38c89a3 100644
--- a/shared/config/init.vendor.rc
+++ b/shared/config/init.vendor.rc
@@ -5,6 +5,7 @@
setprop ro.sf.lcd_density ${ro.boot.lcd_density}
setprop ro.hardware.egl ${ro.boot.hardware.egl}
+ setprop debug.sf.vsync_reactor_ignore_present_fences true
setprop ro.hardware.gralloc ${ro.boot.hardware.gralloc}
setprop ro.hardware.hwcomposer ${ro.boot.hardware.hwcomposer}
setprop ro.hardware.vulkan ${ro.boot.hardware.vulkan}
@@ -12,7 +13,7 @@
setprop ro.hw_timeout_multiplier ${ro.boot.hw_timeout_multiplier}
# start module load in the background
- start vendor.insmod_sh
+ start vendor.dlkm_loader
on init
# ZRAM setup
@@ -46,15 +47,17 @@
mount_all --early
restorecon_recursive /vendor
- start setup_wifi
# works around framework netiface enumeration issue
- start rename_eth1
+ start rename_eth0
start bt_vhci_forwarder
# So GceBootReporter can print to kmsg
chmod 622 /dev/kmsg
+on fs && property:ro.vendor.wifi_impl=virt_wifi
+ start setup_wifi
+
on post-fs
# set RLIMIT_MEMLOCK to 64MB
setrlimit 8 67108864 67108864
@@ -64,20 +67,29 @@
mkdir /data/vendor/radio 0777 system system
on late-fs
- # Wait for keymaster
- exec_start wait_for_keymaster
-
# Mount RW partitions which need run fsck
mount_all --late
write /dev/kmsg "GUEST_BUILD_FINGERPRINT: ${ro.build.fingerprint}"
+on post-fs-data && property:ro.vendor.wifi_impl=mac8011_hwsim_virtio
+ setprop vold.post_fs_data_done 1
+ start wifi-net-sh
+
on boot
chmod 0660 /dev/cpuctl
mkdir /data/vendor/wifi 0770 wifi wifi
mkdir /data/vendor/wifi/wpa 0770 wifi wifi
mkdir /data/vendor/wifi/wpa/sockets 0770 wifi wifi
start socket_vsock_proxy
+ setprop ro.hardware.audio.primary goldfish
+
+service wifi-net-sh /vendor/bin/init.wifi.sh
+ class late_start
+ user root
+ group root wakelock wifi
+ oneshot
+ disabled # Started on post-fs-data
service bt_vhci_forwarder /vendor/bin/bt_vhci_forwarder -virtio_console_dev=/dev/hvc5
user bluetooth
@@ -86,7 +98,7 @@
service setup_wifi /vendor/bin/setup_wifi
oneshot
-service rename_eth1 /vendor/bin/rename_netiface eth1 rmnet0
+service rename_eth0 /vendor/bin/rename_netiface eth0 rmnet0
oneshot
on property:sys.boot_completed=1
@@ -99,7 +111,7 @@
on sys-boot-completed-set && property:persist.sys.zram_enabled=1
swapon_all
-service vendor.insmod_sh /vendor/bin/init.insmod.sh
+service vendor.dlkm_loader /vendor/bin/dlkm_loader
class main
user root
group root system
diff --git a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_0.idc b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_0.idc
new file mode 100644
index 0000000..8a03307
--- /dev/null
+++ b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_0.idc
@@ -0,0 +1,6 @@
+device.internal = 1
+
+touch.deviceType = touchScreen
+touch.orientationAware = 1
+
+# touch.displayId = local:8141241408256768
diff --git a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_1.idc b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_1.idc
new file mode 100644
index 0000000..1e57818
--- /dev/null
+++ b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_1.idc
@@ -0,0 +1,6 @@
+device.internal = 1
+
+touch.deviceType = touchScreen
+touch.orientationAware = 1
+
+touch.displayId = local:8141533520759297
diff --git a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_2.idc b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_2.idc
new file mode 100644
index 0000000..c9d6a8f
--- /dev/null
+++ b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_2.idc
@@ -0,0 +1,6 @@
+device.internal = 1
+
+touch.deviceType = touchScreen
+touch.orientationAware = 1
+
+touch.displayId = local:8141106354454786
diff --git a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_3.idc b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_3.idc
new file mode 100644
index 0000000..a133e24
--- /dev/null
+++ b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_3.idc
@@ -0,0 +1,6 @@
+device.internal = 1
+
+touch.deviceType = touchScreen
+touch.orientationAware = 1
+
+touch.displayId = local:8141521668171267
diff --git a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_4.idc b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_4.idc
new file mode 100644
index 0000000..c32fb3c
--- /dev/null
+++ b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_4.idc
@@ -0,0 +1,6 @@
+device.internal = 1
+
+touch.deviceType = touchScreen
+touch.orientationAware = 1
+
+touch.displayId = local:8141140893513220
diff --git a/shared/config/manifest.xml b/shared/config/manifest.xml
index b14b82c..d48ac0b 100644
--- a/shared/config/manifest.xml
+++ b/shared/config/manifest.xml
@@ -16,16 +16,7 @@
** limitations under the License.
*/
-->
-<manifest version="1.0" type="device" target-level="6">
- <hal format="hidl">
- <name>android.hardware.audio</name>
- <transport>hwbinder</transport>
- <version>6.0</version>
- <interface>
- <name>IDevicesFactory</name>
- <instance>default</instance>
- </interface>
- </hal>
+<manifest version="1.0" type="device" target-level="7">
<hal format="hidl">
<name>android.hardware.audio.effect</name>
<transport>hwbinder</transport>
@@ -97,11 +88,10 @@
</interface>
</hal>
-->
- <!-- TODO (b/130079341): -->
<hal format="hidl">
<name>android.hardware.graphics.composer</name>
<transport>hwbinder</transport>
- <version>2.2</version>
+ <version>2.3</version>
<interface>
<name>IComposer</name>
<instance>default</instance>
diff --git a/shared/config/ueventd.rc b/shared/config/ueventd.rc
index 3d5a219..8f24201 100644
--- a/shared/config/ueventd.rc
+++ b/shared/config/ueventd.rc
@@ -17,7 +17,7 @@
/dev/hvc2 0660 system logd
# keymaster
-/dev/hvc3 0660 system system
+/dev/hvc3 0666 system system
# gatekeeper
/dev/hvc4 0660 system system
diff --git a/shared/device.mk b/shared/device.mk
index 5b319cb..70753ba 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -26,12 +26,21 @@
# Enforce generic ramdisk allow list
$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_ramdisk.mk)
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for vulkan
+# Set Vendor SPL to match platform
+VENDOR_SECURITY_PATCH = $(PLATFORM_SECURITY_PATCH)
-PRODUCT_SHIPPING_API_LEVEL := 31
+# Set boot SPL
+BOOT_SECURITY_PATCH = $(PLATFORM_SECURITY_PATCH)
+
+PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for vulkan
+PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for audio and wifi
+
+PRODUCT_SHIPPING_API_LEVEL := 32
PRODUCT_USE_DYNAMIC_PARTITIONS := true
DISABLE_RILD_OEM_HOOK := true
+PRODUCT_SET_DEBUGFS_RESTRICTIONS := true
+
PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for vulkan
TARGET_RO_FILE_SYSTEM_TYPE ?= ext4
@@ -42,6 +51,9 @@
TARGET_ENABLE_HOST_BLUETOOTH_EMULATION ?= true
TARGET_USE_BTLINUX_HAL_IMPL ?= true
+# TODO(b/65201432): Swiftshader needs to create executable memory.
+PRODUCT_REQUIRES_INSECURE_EXECMEM_FOR_SWIFTSHADER := true
+
AB_OTA_UPDATER := true
AB_OTA_PARTITIONS += \
boot \
@@ -57,7 +69,7 @@
vendor_dlkm \
# Enable Virtual A/B
-$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression_with_xor.mk)
# Enable Scoped Storage related
$(call inherit-product, $(SRC_TARGET_DIR)/product/emulated_storage.mk)
@@ -135,6 +147,9 @@
# DRM service opt-in
PRODUCT_VENDOR_PROPERTIES += drm.service.enabled=true
+# Call deleteAllKeys if vold detects a factory reset
+PRODUCT_VENDOR_PROPERTIES += ro.crypto.metadata_init_delete_all_keys.enabled=true
+
PRODUCT_SOONG_NAMESPACES += hardware/google/camera
PRODUCT_SOONG_NAMESPACES += hardware/google/camera/devices/EmulatedCamera
@@ -143,9 +158,8 @@
#
PRODUCT_PACKAGES += \
CuttlefishService \
- cuttlefish_rotate \
+ cuttlefish_sensor_injection \
rename_netiface \
- setup_wifi \
bt_vhci_forwarder \
socket_vsock_proxy \
tombstone_transmit \
@@ -189,12 +203,6 @@
libGLESv2_angle \
libfeature_support_angle.so
-# SwiftShader provides a software-only implementation that is not thread-safe
-PRODUCT_PACKAGES += \
- libEGL_swiftshader \
- libGLESv1_CM_swiftshader \
- libGLESv2_swiftshader
-
# GL implementation for virgl
PRODUCT_PACKAGES += \
libGLES_mesa \
@@ -220,14 +228,17 @@
libEGL_emulation \
libGLESv2_enc \
libGLESv2_emulation \
- libGLESv1_enc
+ libGLESv1_enc \
+ libGoldfishProfiler \
#
# Packages for testing
#
PRODUCT_PACKAGES += \
aidl_lazy_test_server \
- hidl_lazy_test_server
+ aidl_lazy_cb_test_server \
+ hidl_lazy_test_server \
+ hidl_lazy_cb_test_server
DEVICE_PACKAGE_OVERLAYS := device/google/cuttlefish/shared/overlay
# PRODUCT_AAPT_CONFIG and PRODUCT_AAPT_PREF_CONFIG are intentionally not set to
@@ -260,7 +271,7 @@
hardware/google/camera/devices/EmulatedCamera/hwl/configs/emu_camera_depth.json:$(TARGET_COPY_OUT_VENDOR)/etc/config/emu_camera_depth.json \
device/google/cuttlefish/shared/config/init.vendor.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw/init.cutf_cvm.rc \
device/google/cuttlefish/shared/config/init.product.rc:$(TARGET_COPY_OUT_PRODUCT)/etc/init/init.rc \
- device/google/cuttlefish/shared/config/ueventd.rc:$(TARGET_COPY_OUT_VENDOR)/ueventd.rc \
+ device/google/cuttlefish/shared/config/ueventd.rc:$(TARGET_COPY_OUT_VENDOR)/etc/ueventd.rc \
device/google/cuttlefish/shared/config/media_codecs.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs.xml \
device/google/cuttlefish/shared/config/media_codecs_google_video.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_video.xml \
device/google/cuttlefish/shared/config/media_codecs_performance.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_performance.xml \
@@ -292,28 +303,25 @@
frameworks/native/data/etc/android.software.sip.voip.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.sip.voip.xml \
frameworks/native/data/etc/android.software.verified_boot.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.verified_boot.xml \
system/bt/vendor_libs/test_vendor_lib/data/controller_properties.json:vendor/etc/bluetooth/controller_properties.json \
- device/google/cuttlefish/shared/config/task_profiles.json:$(TARGET_COPY_OUT_VENDOR)/etc/task_profiles.json
+ device/google/cuttlefish/shared/config/task_profiles.json:$(TARGET_COPY_OUT_VENDOR)/etc/task_profiles.json \
+ device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_0.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_0.idc \
+ device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_1.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_1.idc \
+ device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_2.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_2.idc \
+ device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_3.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_3.idc \
+ device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_4.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_4.idc
ifeq ($(TARGET_RO_FILE_SYSTEM_TYPE),ext4)
PRODUCT_COPY_FILES += \
device/google/cuttlefish/shared/config/fstab.f2fs:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.f2fs \
- device/google/cuttlefish/shared/config/fstab.f2fs:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/fstab.f2fs \
device/google/cuttlefish/shared/config/fstab.f2fs:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.f2fs \
- device/google/cuttlefish/shared/config/fstab.f2fs:$(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/fstab.f2fs \
device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.ext4 \
- device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/fstab.ext4 \
- device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.ext4 \
- device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/fstab.ext4
+ device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.ext4
else
PRODUCT_COPY_FILES += \
device/google/cuttlefish/shared/config/fstab-$(TARGET_RO_FILE_SYSTEM_TYPE).f2fs:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.f2fs \
- device/google/cuttlefish/shared/config/fstab-$(TARGET_RO_FILE_SYSTEM_TYPE).f2fs:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/fstab.f2fs \
device/google/cuttlefish/shared/config/fstab-$(TARGET_RO_FILE_SYSTEM_TYPE).f2fs:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.f2fs \
- device/google/cuttlefish/shared/config/fstab-$(TARGET_RO_FILE_SYSTEM_TYPE).f2fs:$(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/fstab.f2fs \
device/google/cuttlefish/shared/config/fstab-$(TARGET_RO_FILE_SYSTEM_TYPE).ext4:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.ext4 \
- device/google/cuttlefish/shared/config/fstab-$(TARGET_RO_FILE_SYSTEM_TYPE).ext4:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/fstab.ext4 \
- device/google/cuttlefish/shared/config/fstab-$(TARGET_RO_FILE_SYSTEM_TYPE).ext4:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.ext4 \
- device/google/cuttlefish/shared/config/fstab-$(TARGET_RO_FILE_SYSTEM_TYPE).ext4:$(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/fstab.ext4
+ device/google/cuttlefish/shared/config/fstab-$(TARGET_RO_FILE_SYSTEM_TYPE).ext4:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.ext4
endif
ifeq ($(TARGET_VULKAN_SUPPORT),true)
@@ -362,8 +370,8 @@
hwcomposer.drm_minigbm \
hwcomposer.cutf \
hwcomposer-stats \
- android.hardware.graphics.composer@2.2-impl \
- android.hardware.graphics.composer@2.2-service
+ android.hardware.graphics.composer@2.3-impl \
+ android.hardware.graphics.composer@2.3-service
#
# Gralloc HAL
@@ -396,16 +404,17 @@
# Audio HAL
#
LOCAL_AUDIO_PRODUCT_PACKAGE ?= \
- audio.primary.cutf \
- audio.r_submix.default \
- android.hardware.audio@6.0-impl \
+ android.hardware.audio.service \
+ android.hardware.audio@6.0-impl.ranchu \
android.hardware.audio.effect@6.0-impl \
- android.hardware.audio@2.0-service
LOCAL_AUDIO_PRODUCT_COPY_FILES ?= \
- device/google/cuttlefish/shared/config/audio_policy.conf:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy.conf \
- frameworks/av/services/audiopolicy/config/audio_policy_configuration_generic.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
- frameworks/av/services/audiopolicy/config/primary_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/primary_audio_policy_configuration.xml
+ device/generic/goldfish/audio/policy/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
+ device/generic/goldfish/audio/policy/primary_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/primary_audio_policy_configuration.xml \
+ frameworks/av/services/audiopolicy/config/r_submix_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/r_submix_audio_policy_configuration.xml \
+ frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_volumes.xml \
+ frameworks/av/services/audiopolicy/config/default_volume_tables.xml:$(TARGET_COPY_OUT_VENDOR)/etc/default_volume_tables.xml \
+ frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \
LOCAL_AUDIO_DEVICE_PACKAGE_OVERLAYS ?=
@@ -449,16 +458,24 @@
#
# Camera
#
+ifeq ($(TARGET_USE_VSOCK_CAMERA_HAL_IMPL),true)
+PRODUCT_PACKAGES += \
+ android.hardware.camera.provider@2.6-external-vsock-service \
+ android.hardware.camera.provider@2.6-impl-cuttlefish
+DEVICE_MANIFEST_FILE += \
+ device/google/cuttlefish/guest/hals/camera/manifest.xml
+else
PRODUCT_PACKAGES += \
android.hardware.camera.provider@2.6-service-google \
libgooglecamerahwl_impl \
android.hardware.camera.provider@2.6-impl-google \
+endif
#
# Gatekeeper
#
ifeq ($(LOCAL_GATEKEEPER_PRODUCT_PACKAGE),)
- LOCAL_GATEKEEPER_PRODUCT_PACKAGE := android.hardware.gatekeeper@1.0-service.software
+ LOCAL_GATEKEEPER_PRODUCT_PACKAGE := android.hardware.gatekeeper@1.0-service.remote
endif
PRODUCT_PACKAGES += \
$(LOCAL_GATEKEEPER_PRODUCT_PACKAGE)
@@ -510,22 +527,17 @@
android.hardware.lights-service.example \
#
-# Keymaster HAL
-#
-ifeq ($(LOCAL_KEYMASTER_PRODUCT_PACKAGE),)
- LOCAL_KEYMASTER_PRODUCT_PACKAGE := android.hardware.keymaster@4.1-service
-endif
-PRODUCT_PACKAGES += \
- $(LOCAL_KEYMASTER_PRODUCT_PACKAGE)
-
-#
# KeyMint HAL
#
ifeq ($(LOCAL_KEYMINT_PRODUCT_PACKAGE),)
- LOCAL_KEYMINT_PRODUCT_PACKAGE := android.hardware.security.keymint-service
+ LOCAL_KEYMINT_PRODUCT_PACKAGE := android.hardware.security.keymint-service.remote
endif
-# PRODUCT_PACKAGES += \
-# $(LOCAL_KEYMINT_PRODUCT_PACKAGE)
+ PRODUCT_PACKAGES += \
+ $(LOCAL_KEYMINT_PRODUCT_PACKAGE)
+
+# Keymint configuration
+PRODUCT_COPY_FILES += \
+ frameworks/native/data/etc/android.software.device_id_attestation.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.device_id_attestation.xml
#
# Power HAL
@@ -553,7 +565,6 @@
android.hardware.neuralnetworks-service-sample-float-slow \
android.hardware.neuralnetworks-service-sample-minimal \
android.hardware.neuralnetworks-service-sample-quant \
- android.hardware.neuralnetworks-shell-service-sample \
android.hardware.neuralnetworks-shim-service-sample
#
@@ -579,14 +590,6 @@
PRODUCT_PACKAGES += \
android.hardware.memtrack-service.example
-# GKI APEX
-PRODUCT_PACKAGES += com.android.gki.kmi_5_10_android12_0
-
-# Prevent GKI and boot image downgrades
-PRODUCT_PRODUCT_PROPERTIES += \
- ro.build.ab_update.gki.prevent_downgrade_version=true \
- ro.build.ab_update.gki.prevent_downgrade_spl=true \
-
# WLAN driver configuration files
PRODUCT_COPY_FILES += \
external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant_template.conf:$(TARGET_COPY_OUT_VENDOR)/etc/wifi/wpa_supplicant.conf \
@@ -617,11 +620,21 @@
PRODUCT_PACKAGES += linker.recovery shell_and_utilities_recovery
endif
-#
-# Shell script Vendor Module Loading
-#
+# wifi
+ifeq ($(PRODUCT_ENFORCE_MAC80211_HWSIM),true)
+PRODUCT_PACKAGES += \
+ mac80211_create_radios \
+ hostapd \
+ android.hardware.wifi@1.0-service
+
PRODUCT_COPY_FILES += \
- $(LOCAL_PATH)/config/init.insmod.sh:$(TARGET_COPY_OUT_VENDOR)/bin/init.insmod.sh \
+ device/google/cuttlefish/guest/services/wifi/init.wifi.sh:$(TARGET_COPY_OUT_VENDOR)/bin/init.wifi.sh \
+
+PRODUCT_VENDOR_PROPERTIES += ro.vendor.wifi_impl=mac8011_hwsim_virtio
+else
+PRODUCT_PACKAGES += setup_wifi
+PRODUCT_VENDOR_PROPERTIES += ro.vendor.wifi_impl=virt_wifi
+endif
# Host packages to install
PRODUCT_HOST_PACKAGES += socket_vsock_proxy
@@ -634,3 +647,6 @@
# with HW VSYNC
PRODUCT_VENDOR_PROPERTIES += \
ro.surface_flinger.running_without_sync_framework=true
+# Vendor Dlkm Locader
+PRODUCT_PACKAGES += \
+ dlkm_loader
diff --git a/shared/permissions/cuttlefish_excluded_hardware.xml b/shared/permissions/cuttlefish_excluded_hardware.xml
index 3660289..c3d03d5 100644
--- a/shared/permissions/cuttlefish_excluded_hardware.xml
+++ b/shared/permissions/cuttlefish_excluded_hardware.xml
@@ -14,7 +14,6 @@
limitations under the License.
-->
<permissions>
- <unavailable-feature name="android.hardware.microphone" />
<unavailable-feature name="android.software.print" />
<unavailable-feature name="android.software.voice_recognizers" />
</permissions>
diff --git a/shared/phone/overlay/frameworks/base/core/res/res/values/config.xml b/shared/phone/overlay/frameworks/base/core/res/res/values/config.xml
index 7ae7588..b6554cc 100644
--- a/shared/phone/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/shared/phone/overlay/frameworks/base/core/res/res/values/config.xml
@@ -65,4 +65,7 @@
<item>2:2:255</item> <!-- ID2:Fingerprint(HIDL):Weak -->
<item>3:8:255</item> <!-- ID3:Face(HIDL):Weak -->
</string-array>
+
+ <!-- Enable Night display, which requires HWC 2.0. -->
+ <bool name="config_nightDisplayAvailable">true</bool>
</resources>
diff --git a/shared/sepolicy/system_ext/private/mediatranscoding.te b/shared/sepolicy/system_ext/private/mediatranscoding.te
index 22e006a..47f6d8e 100644
--- a/shared/sepolicy/system_ext/private/mediatranscoding.te
+++ b/shared/sepolicy/system_ext/private/mediatranscoding.te
@@ -1,8 +1,2 @@
-# Allow mediatranscoding service to access media-related system properties
-get_prop(mediatranscoding, media_config_prop)
-
-# Allow mediatranscoding service to use DMA-BUF
-allow mediatranscoding dmabuf_system_heap_device:chr_file r_file_perms;
-
# Allow mediatranscoding service to access the GPU
gpu_access(mediatranscoding)
diff --git a/shared/sepolicy/vendor/bluetooth.te b/shared/sepolicy/vendor/bluetooth.te
new file mode 100644
index 0000000..aa2671a
--- /dev/null
+++ b/shared/sepolicy/vendor/bluetooth.te
@@ -0,0 +1 @@
+gpu_access(bluetooth)
diff --git a/shared/sepolicy/vendor/bug_map b/shared/sepolicy/vendor/bug_map
index 32a0b4c..46ef531 100644
--- a/shared/sepolicy/vendor/bug_map
+++ b/shared/sepolicy/vendor/bug_map
@@ -2,4 +2,3 @@
init system_lib_file file b/133444385
kernel kernel capability b/179966921
migrate_legacy_obb_data dalvikcache_data_file file b/152338071
-system_server system_server process b/65201432
diff --git a/shared/sepolicy/vendor/cuttlefish_rotate.te b/shared/sepolicy/vendor/cuttlefish_rotate.te
deleted file mode 100644
index 7d8a9a1..0000000
--- a/shared/sepolicy/vendor/cuttlefish_rotate.te
+++ /dev/null
@@ -1,15 +0,0 @@
-type cuttlefish_rotate, domain;
-type cuttlefish_rotate_exec, exec_type, vendor_file_type, file_type;
-
-# Switch to cuttlefish_rotate domain when executing from shell.
-domain_auto_trans(shell, cuttlefish_rotate_exec, cuttlefish_rotate)
-allow cuttlefish_rotate shell:fd use;
-
-# Allow cuttlefish_rotate to communicate over adb connection.
-allow cuttlefish_rotate adbd:fd use;
-allow cuttlefish_rotate adbd:unix_stream_socket { read write };
-# Needed to run the binary directly via adb socket.
-allow cuttlefish_rotate devpts:chr_file { read write };
-
-# Grant cuttlefish_rotate access to the ISensors HAL.
-hal_client_domain(cuttlefish_rotate, hal_sensors)
diff --git a/shared/sepolicy/vendor/cuttlefish_sensor_injection.te b/shared/sepolicy/vendor/cuttlefish_sensor_injection.te
new file mode 100644
index 0000000..9e7aca5
--- /dev/null
+++ b/shared/sepolicy/vendor/cuttlefish_sensor_injection.te
@@ -0,0 +1,15 @@
+type cuttlefish_sensor_injection, domain;
+type cuttlefish_sensor_injection_exec, exec_type, vendor_file_type, file_type;
+
+# Switch to cuttlefish_sensor_injection domain when executing from shell.
+domain_auto_trans(shell, cuttlefish_sensor_injection_exec, cuttlefish_sensor_injection)
+allow cuttlefish_sensor_injection shell:fd use;
+
+# Allow cuttlefish_sensor_injection to communicate over adb connection.
+allow cuttlefish_sensor_injection adbd:fd use;
+allow cuttlefish_sensor_injection adbd:unix_stream_socket { read write };
+# Needed to run the binary directly via adb socket.
+allow cuttlefish_sensor_injection devpts:chr_file { read write };
+
+# Grant cuttlefish_sensor_injection access to the ISensors HAL.
+hal_client_domain(cuttlefish_sensor_injection, hal_sensors)
diff --git a/shared/sepolicy/vendor/device.te b/shared/sepolicy/vendor/device.te
index b698e35..fe42c1f 100644
--- a/shared/sepolicy/vendor/device.te
+++ b/shared/sepolicy/vendor/device.te
@@ -1,2 +1,2 @@
# Device types
-type ab_block_device, dev_type;
+type ab_block_device, dev_type, bdev_type;
diff --git a/shared/sepolicy/vendor/dlkm_loader.te b/shared/sepolicy/vendor/dlkm_loader.te
new file mode 100644
index 0000000..afdc65c
--- /dev/null
+++ b/shared/sepolicy/vendor/dlkm_loader.te
@@ -0,0 +1,16 @@
+type dlkm_loader, domain;
+type dlkm_loader_exec, exec_type, vendor_file_type, file_type;
+
+init_daemon_domain(dlkm_loader)
+
+# Allow insmod on vendor and system partitions
+allow dlkm_loader self:capability sys_module;
+allow dlkm_loader system_file:system module_load;
+allow dlkm_loader vendor_file:system module_load;
+
+# needed for libmodprobe to read kernel commandline
+allow dlkm_loader proc_cmdline:file r_file_perms;
+
+# dlkm_loader searches tracefs while looking for modules
+dontaudit dlkm_loader debugfs_bootreceiver_tracing:dir search;
+dontaudit dlkm_loader debugfs_mm_events_tracing:dir search;
diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts
index 23f3f60..72362dc 100644
--- a/shared/sepolicy/vendor/file_contexts
+++ b/shared/sepolicy/vendor/file_contexts
@@ -56,12 +56,14 @@
#############################
# Vendor files
#
-/vendor/bin/cuttlefish_rotate u:object_r:cuttlefish_rotate_exec:s0
+/vendor/bin/cuttlefish_sensor_injection u:object_r:cuttlefish_sensor_injection_exec:s0
+/vendor/bin/mac80211_create_radios u:object_r:mac80211_create_radios_exec:s0
/vendor/bin/socket_vsock_proxy u:object_r:socket_vsock_proxy_exec:s0
/vendor/bin/vsoc_input_service u:object_r:vsoc_input_service_exec:s0
/vendor/bin/rename_netiface u:object_r:rename_netiface_exec:s0
/vendor/bin/suspend_blocker u:object_r:suspend_blocker_exec:s0
/vendor/bin/hw/libcuttlefish-rild u:object_r:libcuttlefish_rild_exec:s0
+/vendor/bin/hw/android\.hardware\.camera\.provider@2\.6-external-vsock-service u:object_r:hal_camera_default_exec:s0
/vendor/bin/hw/android\.hardware\.camera\.provider@2\.6-service-google u:object_r:hal_camera_default_exec:s0
/vendor/bin/hw/android\.hardware\.camera\.provider@2\.6-service-google-lazy u:object_r:hal_camera_default_exec:s0
/vendor/bin/hw/android\.hardware\.power\.stats@1\.0-service\.mock u:object_r:hal_power_stats_default_exec:s0
@@ -77,7 +79,6 @@
/vendor/bin/hw/android\.hardware\.health\.storage-service\.cuttlefish u:object_r:hal_health_storage_default_exec:s0
/vendor/bin/hw/android\.hardware\.lights-service\.example u:object_r:hal_light_default_exec:s0
/vendor/bin/hw/android\.hardware\.neuralnetworks@1\.3-service-sample-.* u:object_r:hal_neuralnetworks_sample_exec:s0
-/vendor/bin/hw/android\.hardware\.neuralnetworks-shell-service-sample u:object_r:hal_neuralnetworks_sample_exec:s0
/vendor/bin/hw/android\.hardware\.neuralnetworks-shim-service-sample u:object_r:hal_neuralnetworks_sample_exec:s0
/vendor/bin/hw/android\.hardware\.neuralnetworks-service-sample-.* u:object_r:hal_neuralnetworks_sample_exec:s0
/vendor/bin/hw/android\.hardware\.vibrator@1\.x-service\.example u:object_r:hal_vibrator_default_exec:s0
@@ -87,6 +88,7 @@
/vendor/bin/hw/android\.hardware\.sensors@2\.1-service\.mock u:object_r:hal_sensors_default_exec:s0
/vendor/bin/hw/android\.hardware\.input\.classifier@1\.0-service.default u:object_r:hal_input_classifier_default_exec:s0
/vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.mock u:object_r:hal_thermal_default_exec:s0
+/vendor/bin/hw/android\.hardware\.security\.keymint-service\.remote u:object_r:hal_keymint_remote_exec:s0
/vendor/bin/hw/android\.hardware\.keymaster@4\.1-service.remote u:object_r:hal_keymaster_remote_exec:s0
/vendor/bin/hw/android\.hardware\.gatekeeper@1\.0-service.remote u:object_r:hal_gatekeeper_remote_exec:s0
/vendor/bin/hw/android\.hardware\.oemlock-service.example u:object_r:hal_oemlock_default_exec:s0
@@ -94,12 +96,14 @@
/vendor/bin/hw/android\.hardware\.authsecret@1\.0-service u:object_r:hal_authsecret_default_exec:s0
/vendor/bin/hw/android\.hardware\.authsecret-service.example u:object_r:hal_authsecret_default_exec:s0
/vendor/bin/hw/android\.hardware\.rebootescrow-service\.default u:object_r:hal_rebootescrow_default_exec:s0
-/vendor/bin/init\.insmod\.sh u:object_r:init_insmod_sh_exec:s0
+/vendor/bin/dlkm_loader u:object_r:dlkm_loader_exec:s0
+/vendor/bin/init\.wifi\.sh u:object_r:init_wifi_sh_exec:s0
/vendor/lib(64)?/libdrm.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libglapi.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/dri/.* u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/hw/android\.hardware\.graphics\.mapper@4\.0-impl\.minigbm\.so u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libminigbm_gralloc.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/hw/android\.hardware\.health@2\.0-impl-2\.1-cuttlefish\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/hw/vulkan.pastel.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libcuttlefish_fs.so u:object_r:same_process_hal_file:s0
@@ -121,3 +125,4 @@
/vendor/lib(64)?/libGLESv1_CM_angle\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libGLESv2_angle\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libfeature_support_angle\.so u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libGoldfishProfiler\.so u:object_r:same_process_hal_file:s0
diff --git a/shared/sepolicy/vendor/genfs_contexts b/shared/sepolicy/vendor/genfs_contexts
index 22cb59f..ef41a43 100644
--- a/shared/sepolicy/vendor/genfs_contexts
+++ b/shared/sepolicy/vendor/genfs_contexts
@@ -30,17 +30,23 @@
dnl
# crosvm (x86)
cf_pci_block_device(/devices/pci0000:00, 0x6, 5)
-cf_pci_gpu_device(/devices/pci0000:00, 0x11)
+cf_pci_gpu_device(/devices/pci0000:00, 0x12)
## find /sys/devices/platform/* -type d -name 'rtc[0-9]' | sed 's,/rtc[0-9],,'
genfscon sysfs /devices/platform/rtc_cmos/rtc u:object_r:sysfs_rtc:s0
## find /sys/devices/platform/* -type d -name 'wakeup[0-9]'
-cf_rtc_wakeup_alarmtimer(/devices/platform/rtc_cmos, 0, 0)
-genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0
-genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup3 u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/wakeup/wakeup0 u:object_r:sysfs_wakeup:s0
+cf_rtc_wakeup_alarmtimer(/devices/platform/rtc_cmos, 0, 1) # wakeup{1,2}
+genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup3 u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup4 u:object_r:sysfs_wakeup:s0
+## currently disabled
+#genfscon sysfs /devices/LNXSYSTM:00/GFSH0001:00/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0
+#genfscon sysfs /devices/platform/GFSH0001:00/power_supply u:object_r:sysfs_batteryinfo:s0
+#genfscon sysfs /devices/platform/GFSH0001:00/power_supply/ac/wakeup3 u:object_r:sysfs_wakeup:s0
+#genfscon sysfs /devices/platform/GFSH0001:00/power_supply/battery/wakeup4 u:object_r:sysfs_wakeup:s0
# crosvm (arm64)
cf_pci_block_device(/devices/platform/10000.pci, 0x6, 4)
-cf_pci_gpu_device(/devices/platform/10000.pci/pci0000:00, 0x11)
+cf_pci_gpu_device(/devices/platform/10000.pci/pci0000:00, 0x10)
## find /sys/devices/platform/* -type d -name 'rtc[0-9]' | sed 's,/rtc[0-9],,'
genfscon sysfs /devices/platform/2000.rtc/rtc u:object_r:sysfs_rtc:s0
## find /sys/devices/platform/* -type d -name 'wakeup[0-9]'
@@ -67,7 +73,7 @@
# qemu (arm)
cf_pci_block_device(/devices/platform/3f000000.pcie/pci0000:00, 0x6, 4)
-cf_pci_gpu_device(/devices/platform/3f000000.pcie/pci0000:00, 0xf)
+cf_pci_gpu_device(/devices/platform/3f000000.pcie/pci0000:00, 0x11)
# common on all platforms / vm managers
genfscon sysfs /devices/platform/rtc-test.0/rtc u:object_r:sysfs_rtc:s0
diff --git a/shared/sepolicy/system_ext/private/mediaprovider.te b/shared/sepolicy/vendor/google/mediaprovider.te
similarity index 100%
rename from shared/sepolicy/system_ext/private/mediaprovider.te
rename to shared/sepolicy/vendor/google/mediaprovider.te
diff --git a/shared/sepolicy/system_ext/private/traceur_app.te b/shared/sepolicy/vendor/google/traceur_app.te
similarity index 100%
rename from shared/sepolicy/system_ext/private/traceur_app.te
rename to shared/sepolicy/vendor/google/traceur_app.te
diff --git a/shared/sepolicy/vendor/hal_camera_default.te b/shared/sepolicy/vendor/hal_camera_default.te
index 6bf571c..e4dac76 100644
--- a/shared/sepolicy/vendor/hal_camera_default.te
+++ b/shared/sepolicy/vendor/hal_camera_default.te
@@ -10,3 +10,6 @@
hal_client_domain(hal_camera_default, hal_thermal)
gpu_access(hal_camera_default)
+
+# Vsocket camera
+allow hal_camera_default self:vsock_socket { accept bind create getopt listen read write };
diff --git a/shared/sepolicy/vendor/hal_keymint_remote.te b/shared/sepolicy/vendor/hal_keymint_remote.te
new file mode 100644
index 0000000..578d1ad
--- /dev/null
+++ b/shared/sepolicy/vendor/hal_keymint_remote.te
@@ -0,0 +1,14 @@
+type hal_keymint_remote, domain;
+hal_server_domain(hal_keymint_remote, hal_keymint)
+
+type hal_keymint_remote_exec, exec_type, vendor_file_type, file_type;
+init_daemon_domain(hal_keymint_remote)
+
+allow hal_keymint_remote device:dir r_dir_perms;
+allow hal_keymint_remote keymaster_device:chr_file rw_file_perms;
+
+# Write to kernel log (/dev/kmsg)
+allow hal_keymint_remote kmsg_device:chr_file w_file_perms;
+allow hal_keymint_remote kmsg_device:chr_file getattr;
+
+get_prop(hal_keymint_remote, vendor_security_patch_level_prop)
diff --git a/shared/sepolicy/vendor/hal_wifi_default.te b/shared/sepolicy/vendor/hal_wifi_default.te
new file mode 100644
index 0000000..cde7ada
--- /dev/null
+++ b/shared/sepolicy/vendor/hal_wifi_default.te
@@ -0,0 +1,3 @@
+allow hal_wifi_default hal_wifi_default:netlink_route_socket {
+ create bind write read nlmsg_read nlmsg_readpriv };
+allow hal_wifi_default self:capability { sys_module };
diff --git a/shared/sepolicy/vendor/init_insmod_sh.te b/shared/sepolicy/vendor/init_insmod_sh.te
deleted file mode 100644
index 5400a37..0000000
--- a/shared/sepolicy/vendor/init_insmod_sh.te
+++ /dev/null
@@ -1,11 +0,0 @@
-type init_insmod_sh, domain;
-type init_insmod_sh_exec, exec_type, vendor_file_type, file_type;
-
-init_daemon_domain(init_insmod_sh)
-
-allow init_insmod_sh vendor_shell_exec:file rx_file_perms;
-allow init_insmod_sh vendor_toolbox_exec:file rx_file_perms;
-
-# Allow insmod
-allow init_insmod_sh self:capability sys_module;
-allow init_insmod_sh vendor_file:system module_load;
diff --git a/shared/sepolicy/vendor/init_wifi_sh.te b/shared/sepolicy/vendor/init_wifi_sh.te
new file mode 100644
index 0000000..331a745
--- /dev/null
+++ b/shared/sepolicy/vendor/init_wifi_sh.te
@@ -0,0 +1,14 @@
+# cuttlefish-setup service: runs init.cuttlefish.sh script
+type init_wifi_sh, domain;
+type init_wifi_sh_exec, vendor_file_type, exec_type, file_type;
+
+init_daemon_domain(init_wifi_sh)
+
+allow init_wifi_sh self:capability { fowner chown net_admin net_raw };
+allow init_wifi_sh vendor_toolbox_exec:file execute_no_trans;
+allow init_wifi_sh mac80211_create_radios_exec:file execute_no_trans;
+
+vendor_internal_prop(vendor_wifi_mac_prefix);
+get_prop(init_wifi_sh, vendor_wifi_mac_prefix);
+
+allow init_wifi_sh self:netlink_generic_socket create_socket_perms_no_ioctl;
diff --git a/shared/sepolicy/vendor/mac80211_create_radios.te b/shared/sepolicy/vendor/mac80211_create_radios.te
new file mode 100644
index 0000000..f7e6b7f
--- /dev/null
+++ b/shared/sepolicy/vendor/mac80211_create_radios.te
@@ -0,0 +1,2 @@
+type mac80211_create_radios, domain;
+type mac80211_create_radios_exec, exec_type, vendor_file_type, file_type;
diff --git a/shared/sepolicy/vendor/property_contexts b/shared/sepolicy/vendor/property_contexts
index ebbe271..fca72f7 100644
--- a/shared/sepolicy/vendor/property_contexts
+++ b/shared/sepolicy/vendor/property_contexts
@@ -9,6 +9,7 @@
ro.boot.vsock_keyboard_port u:object_r:vendor_vsock_keyboard_port:s0
ro.boot.modem_simulator_ports u:object_r:vendor_modem_simulator_ports_prop:s0
ro.boot.vsock_touch_port u:object_r:vendor_vsock_touch_port:s0
-ro.boot.wifi_mac_address u:object_r:vendor_wifi_mac_address:s0
+ro.boot.wifi_mac_prefix u:object_r:vendor_wifi_mac_prefix:s0 exact string
+ro.vendor.wifi_impl u:object_r:vendor_wifi_impl:s0 exact string
vendor.bt.rootcanal_mac_address u:object_r:vendor_bt_rootcanal_prop:s0
vendor.bt.rootcanal_test_console u:object_r:vendor_bt_rootcanal_prop:s0
diff --git a/shared/sepolicy/vendor/service_contexts b/shared/sepolicy/vendor/service_contexts
index 03b768c..d20d026 100644
--- a/shared/sepolicy/vendor/service_contexts
+++ b/shared/sepolicy/vendor/service_contexts
@@ -4,7 +4,6 @@
android.hardware.neuralnetworks.IDevice/nnapi-sample_minimal u:object_r:hal_neuralnetworks_service:s0
android.hardware.neuralnetworks.IDevice/nnapi-sample_quant u:object_r:hal_neuralnetworks_service:s0
android.hardware.neuralnetworks.IDevice/nnapi-sample_sl_shim u:object_r:hal_neuralnetworks_service:s0
-android.hardware.neuralnetworks.IDevice/nnapi-sample_sl_updatable u:object_r:hal_neuralnetworks_service:s0
# Binder service mappings
gce u:object_r:gce_service:s0
diff --git a/shared/sepolicy/vendor/setup_wifi.te b/shared/sepolicy/vendor/setup_wifi.te
index 23a34eb..b61d4be 100644
--- a/shared/sepolicy/vendor/setup_wifi.te
+++ b/shared/sepolicy/vendor/setup_wifi.te
@@ -9,6 +9,4 @@
allow setup_wifi kernel:system module_request;
-vendor_internal_prop(vendor_wifi_mac_address)
-
-get_prop(setup_wifi, vendor_wifi_mac_address)
+get_prop(setup_wifi, vendor_wifi_mac_prefix)
diff --git a/shared/sepolicy/vendor/shell.te b/shared/sepolicy/vendor/shell.te
index 60d15fa..cc26032 100644
--- a/shared/sepolicy/vendor/shell.te
+++ b/shared/sepolicy/vendor/shell.te
@@ -1,5 +1,5 @@
allow shell serial_device:chr_file { getattr ioctl read write };
-allow shell cuttlefish_rotate_exec:file rx_file_perms;
+allow shell cuttlefish_sensor_injection_exec:file rx_file_perms;
# TODO(b/130668487): Label the vsock sockets.
allow shell adbd:{ socket vsock_socket } rw_socket_perms_no_ioctl;
diff --git a/shared/sepolicy/vendor/system_server.te b/shared/sepolicy/vendor/system_server.te
index 372ca50..1df4c7d 100644
--- a/shared/sepolicy/vendor/system_server.te
+++ b/shared/sepolicy/vendor/system_server.te
@@ -1,11 +1,7 @@
-# TODO(b/65201432): Switch into enforcing mode once execmem issue due to OpenGL is resolved. Also
-# remove the corresponding dontaudit.
-# The current (at the time of writing) implementation of OpenGL needs to create executable memory.
-# Unfortunately, we cannot grant execmem power using an allow rule because global policy
-# (system/sepolicy) contains a corresponding neverallow which would cause build-time errors if the
-# allow execmem rule were added here.
-permissive system_server;
gpu_access(system_server)
# Cuttlefish is still using the legacy wifi HAL (pre-HIDL)
get_prop(system_server, wifi_hal_prop)
+
+# TODO(b/65201432): Swiftshader needs to create executable memory.
+allow system_server self:process execmem;
diff --git a/shared/sepolicy/vendor/vendor_init.te b/shared/sepolicy/vendor/vendor_init.te
index 37e76e1..bcda4a3 100644
--- a/shared/sepolicy/vendor/vendor_init.te
+++ b/shared/sepolicy/vendor/vendor_init.te
@@ -7,3 +7,6 @@
set_prop(vendor_init, vendor_bt_rootcanal_prop)
get_prop(vendor_init, vendor_graphics_config_prop)
+
+vendor_internal_prop(vendor_wifi_impl)
+set_prop(vendor_init, vendor_wifi_impl)
diff --git a/shared/tv/device.mk b/shared/tv/device.mk
index 52e1e38..487c16a 100644
--- a/shared/tv/device.mk
+++ b/shared/tv/device.mk
@@ -28,6 +28,7 @@
frameworks/native/data/etc/android.hardware.hdmi.cec.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.hdmi.cec.xml \
frameworks/native/data/etc/android.hardware.sensor.accelerometer.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.accelerometer.xml \
frameworks/native/data/etc/android.hardware.sensor.compass.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.compass.xml \
+ hardware/interfaces/tv/tuner/config/sample_tuner_vts_config.xml:$(TARGET_COPY_OUT_VENDOR)/etc/tuner_vts_config.xml \
# HDMI CEC HAL
PRODUCT_PACKAGES += android.hardware.tv.cec@1.0-service.mock
diff --git a/tests/hal/hal_implementation_test.cpp b/tests/hal/hal_implementation_test.cpp
index d94d56f..554f01f 100644
--- a/tests/hal/hal_implementation_test.cpp
+++ b/tests/hal/hal_implementation_test.cpp
@@ -17,12 +17,13 @@
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <gtest/gtest.h>
-#include <hidl/metadata.h>
#include <hidl-util/FQName.h>
+#include <hidl/metadata.h>
#include <vintf/VintfObject.h>
using namespace android;
+// clang-format off
static const std::set<std::string> kKnownMissingHidl = {
"android.frameworks.bufferhub@1.0",
"android.frameworks.cameraservice.device@2.0",
@@ -64,6 +65,7 @@
"android.hardware.health.storage@1.0", // converted to AIDL, see b/177470478
"android.hardware.ir@1.0",
"android.hardware.keymaster@3.0",
+ "android.hardware.keymaster@4.1", // Replaced by KeyMint
"android.hardware.light@2.0",
"android.hardware.media.bufferpool@1.0",
"android.hardware.media.bufferpool@2.0",
@@ -94,215 +96,272 @@
"android.hidl.base@1.0",
"android.hidl.memory.token@1.0",
};
+// clang-format on
-static const std::set<std::string> kKnownMissingAidl = {
+struct VersionedAidlPackage {
+ std::string name;
+ size_t version;
+ bool operator<(const VersionedAidlPackage& rhs) const {
+ return (name < rhs.name || (name == rhs.name && version < rhs.version));
+ }
+};
+
+static const std::set<VersionedAidlPackage> kKnownMissingAidl = {
// types-only packages, which never expect a default implementation
- "android.hardware.common.",
- "android.hardware.common.fmq.",
- "android.hardware.graphics.common.",
+ {"android.hardware.common.", 1},
+ {"android.hardware.common.", 2},
+ {"android.hardware.common.fmq.", 1},
+ {"android.hardware.graphics.common.", 1},
+ {"android.hardware.graphics.common.", 2},
// These KeyMaster types are in an AIDL types-only HAL because they're used
// by the Identity Credential AIDL HAL. Remove this when fully porting
// KeyMaster to AIDL.
- "android.hardware.keymaster.",
-
- // Temporarily disable keymint, secureclock, and shared secret in favor of
- // keymaster 4.1. This is required for the transition to Keystore 2.0.
- // Software keymint does not work with Gatekeeper. This can be removed when
- // the remote keymaster implementation was ported to keymint.
- // b/182928606
- "android.hardware.security.keymint.",
- "android.hardware.security.secureclock.",
- "android.hardware.security.sharedsecret.",
+ {"android.hardware.keymaster.", 1},
+ {"android.hardware.keymaster.", 2},
+ {"android.hardware.keymaster.", 3},
// These types are only used in Automotive.
- "android.automotive.computepipe.registry.",
- "android.automotive.computepipe.runner.",
- "android.automotive.watchdog.",
- "android.hardware.automotive.occupant_awareness.",
+ {"android.automotive.computepipe.registry.", 1},
+ {"android.automotive.computepipe.runner.", 1},
+ {"android.automotive.watchdog.", 2},
+ {"android.hardware.automotive.occupant_awareness.", 1},
+
+ // This version needs to be implemented (b/190236358)
+ {"android.hardware.vibrator.", 2},
};
// AOSP packages which are never considered
static bool isHidlPackageConsidered(const FQName& name) {
- static std::vector<std::string> gAospExclude = {
- // packages not implemented now that we never expect to be implemented
- "android.hardware.tests",
- // packages not registered with hwservicemanager, usually sub-interfaces
- "android.hardware.camera.device",
- };
- for (const std::string& package : gAospExclude) {
- if (name.inPackage(package)) {
- return false;
- }
+ static std::vector<std::string> gAospExclude = {
+ // packages not implemented now that we never expect to be implemented
+ "android.hardware.tests",
+ // packages not registered with hwservicemanager, usually sub-interfaces
+ "android.hardware.camera.device",
+ };
+ for (const std::string& package : gAospExclude) {
+ if (name.inPackage(package)) {
+ return false;
}
- return true;
+ }
+ return true;
}
static bool isAospHidlInterface(const FQName& name) {
- static const std::vector<std::string> kAospPackages = {
- "android.hidl",
- "android.hardware",
- "android.frameworks",
- "android.system",
- };
- for (const std::string& package : kAospPackages) {
- if (name.inPackage(package)) {
- return true;
- }
+ static const std::vector<std::string> kAospPackages = {
+ "android.hidl",
+ "android.hardware",
+ "android.frameworks",
+ "android.system",
+ };
+ for (const std::string& package : kAospPackages) {
+ if (name.inPackage(package)) {
+ return true;
}
- return false;
+ }
+ return false;
}
static std::set<FQName> allTreeHidlInterfaces() {
- std::set<FQName> ret;
- for (const auto& iface : HidlInterfaceMetadata::all()) {
- FQName f;
- CHECK(f.setTo(iface.name)) << iface.name;
- ret.insert(f);
- }
- return ret;
+ std::set<FQName> ret;
+ for (const auto& iface : HidlInterfaceMetadata::all()) {
+ FQName f;
+ CHECK(f.setTo(iface.name)) << iface.name;
+ ret.insert(f);
+ }
+ return ret;
}
static std::set<FQName> allHidlManifestInterfaces() {
- std::set<FQName> ret;
- auto setInserter = [&] (const vintf::ManifestInstance& i) -> bool {
- if (i.format() != vintf::HalFormat::HIDL) {
- return true; // continue
- }
- ret.insert(i.getFqInstance().getFqName());
- return true; // continue
- };
- vintf::VintfObject::GetDeviceHalManifest()->forEachInstance(setInserter);
- vintf::VintfObject::GetFrameworkHalManifest()->forEachInstance(setInserter);
- return ret;
+ std::set<FQName> ret;
+ auto setInserter = [&](const vintf::ManifestInstance& i) -> bool {
+ if (i.format() != vintf::HalFormat::HIDL) {
+ return true; // continue
+ }
+ ret.insert(i.getFqInstance().getFqName());
+ return true; // continue
+ };
+ vintf::VintfObject::GetDeviceHalManifest()->forEachInstance(setInserter);
+ vintf::VintfObject::GetFrameworkHalManifest()->forEachInstance(setInserter);
+ return ret;
}
static bool isAospAidlInterface(const std::string& name) {
- return base::StartsWith(name, "android.") &&
- !base::StartsWith(name, "android.hardware.tests.") &&
- !base::StartsWith(name, "android.aidl.tests");
+ return base::StartsWith(name, "android.") &&
+ !base::StartsWith(name, "android.hardware.tests.") &&
+ !base::StartsWith(name, "android.aidl.tests");
}
-static std::set<std::string> allAidlManifestInterfaces() {
- std::set<std::string> ret;
- auto setInserter = [&] (const vintf::ManifestInstance& i) -> bool {
- if (i.format() != vintf::HalFormat::AIDL) {
- return true; // continue
- }
- ret.insert(i.package() + "." + i.interface());
- return true; // continue
- };
- vintf::VintfObject::GetDeviceHalManifest()->forEachInstance(setInserter);
- vintf::VintfObject::GetFrameworkHalManifest()->forEachInstance(setInserter);
- return ret;
+static std::set<VersionedAidlPackage> allAidlManifestInterfaces() {
+ std::set<VersionedAidlPackage> ret;
+ auto setInserter = [&](const vintf::ManifestInstance& i) -> bool {
+ if (i.format() != vintf::HalFormat::AIDL) {
+ return true; // continue
+ }
+ ret.insert({i.package() + "." + i.interface(), i.version().minorVer});
+ return true; // continue
+ };
+ vintf::VintfObject::GetDeviceHalManifest()->forEachInstance(setInserter);
+ vintf::VintfObject::GetFrameworkHalManifest()->forEachInstance(setInserter);
+ return ret;
}
TEST(Hal, AllHidlInterfacesAreInAosp) {
- for (const FQName& name : allHidlManifestInterfaces()) {
- EXPECT_TRUE(isAospHidlInterface(name)) << name.string();
- }
+ for (const FQName& name : allHidlManifestInterfaces()) {
+ EXPECT_TRUE(isAospHidlInterface(name))
+ << "This device should only have AOSP interfaces, not: "
+ << name.string();
+ }
}
TEST(Hal, HidlInterfacesImplemented) {
- // instances -> major version -> minor versions
- std::map<std::string, std::map<size_t, std::set<size_t>>> unimplemented;
+ // instances -> major version -> minor versions
+ std::map<std::string, std::map<size_t, std::set<size_t>>> unimplemented;
- for (const FQName& f : allTreeHidlInterfaces()) {
- if (!isAospHidlInterface(f)) continue;
- if (!isHidlPackageConsidered(f)) continue;
+ for (const FQName& f : allTreeHidlInterfaces()) {
+ if (!isAospHidlInterface(f)) continue;
+ if (!isHidlPackageConsidered(f)) continue;
- unimplemented[f.package()][f.getPackageMajorVersion()].insert(f.getPackageMinorVersion());
+ unimplemented[f.package()][f.getPackageMajorVersion()].insert(
+ f.getPackageMinorVersion());
+ }
+
+ // we'll be removing items from this which we know are missing
+ // in order to be left with those elements which we thought we
+ // knew were missing but are actually present
+ std::set<std::string> thoughtMissing = kKnownMissingHidl;
+
+ for (const FQName& f : allHidlManifestInterfaces()) {
+ if (thoughtMissing.erase(f.getPackageAndVersion().string()) > 0) {
+ ADD_FAILURE() << "Instance in missing list, but available: "
+ << f.string();
}
- // we'll be removing items from this which we know are missing
- // in order to be left with those elements which we thought we
- // knew were missing but are actually present
- std::set<std::string> thoughtMissing = kKnownMissingHidl;
+ std::set<size_t>& minors =
+ unimplemented[f.package()][f.getPackageMajorVersion()];
+ size_t minor = f.getPackageMinorVersion();
- for (const FQName& f : allHidlManifestInterfaces()) {
- if (thoughtMissing.erase(f.getPackageAndVersion().string()) > 0) {
- ADD_FAILURE() << "Instance in missing list, but available: " << f.string();
- }
+ auto it = minors.find(minor);
+ if (it == minors.end()) continue;
- std::set<size_t>& minors = unimplemented[f.package()][f.getPackageMajorVersion()];
- size_t minor = f.getPackageMinorVersion();
+ // if 1.2 is implemented, also considere 1.0, 1.1 implemented
+ minors.erase(minors.begin(), std::next(it));
+ }
- auto it = minors.find(minor);
- if (it == minors.end()) continue;
+ for (const auto& [package, minorsPerMajor] : unimplemented) {
+ for (const auto& [major, minors] : minorsPerMajor) {
+ if (minors.empty()) continue;
- // if 1.2 is implemented, also considere 1.0, 1.1 implemented
- minors.erase(minors.begin(), std::next(it));
+ size_t maxMinor = *minors.rbegin();
+
+ FQName missing;
+ ASSERT_TRUE(missing.setTo(package, major, maxMinor));
+
+ if (thoughtMissing.erase(missing.string()) > 0) continue;
+
+ ADD_FAILURE() << "Missing implementation from " << missing.string();
}
+ }
- for (const auto& [package, minorsPerMajor] : unimplemented) {
- for (const auto& [major, minors] : minorsPerMajor) {
- if (minors.empty()) continue;
-
- size_t maxMinor = *minors.rbegin();
-
- FQName missing;
- ASSERT_TRUE(missing.setTo(package, major, maxMinor));
-
- if (thoughtMissing.erase(missing.string()) > 0) continue;
-
- ADD_FAILURE() << "Missing implementation from " << missing.string();
- }
- }
-
- for (const std::string& missing : thoughtMissing) {
- ADD_FAILURE() << "Instance in missing list and cannot find it anywhere: " << missing
- << " (multiple versions in missing list?)";
- }
+ for (const std::string& missing : thoughtMissing) {
+ ADD_FAILURE() << "Instance in missing list and cannot find it anywhere: "
+ << missing << " (multiple versions in missing list?)";
+ }
}
TEST(Hal, AllAidlInterfacesAreInAosp) {
- for (const std::string& name : allAidlManifestInterfaces()) {
- EXPECT_TRUE(isAospAidlInterface(name)) << name;
- }
+ for (const auto& package : allAidlManifestInterfaces()) {
+ EXPECT_TRUE(isAospAidlInterface(package.name))
+ << "This device should only have AOSP interfaces, not: "
+ << package.name;
+ }
}
// android.hardware.foo.IFoo -> android.hardware.foo.
std::string getAidlPackage(const std::string& aidlType) {
- size_t lastDot = aidlType.rfind('.');
- CHECK(lastDot != std::string::npos);
- return aidlType.substr(0, lastDot + 1);
+ size_t lastDot = aidlType.rfind('.');
+ CHECK(lastDot != std::string::npos);
+ return aidlType.substr(0, lastDot + 1);
}
+struct AidlPackageCheck {
+ bool hasRegistration;
+ bool knownMissing;
+};
+
TEST(Hal, AidlInterfacesImplemented) {
- std::set<std::string> manifest = allAidlManifestInterfaces();
- std::set<std::string> thoughtMissing = kKnownMissingAidl;
+ std::set<VersionedAidlPackage> manifest = allAidlManifestInterfaces();
+ std::set<VersionedAidlPackage> thoughtMissing = kKnownMissingAidl;
- for (const auto& iface : AidlInterfaceMetadata::all()) {
- ASSERT_FALSE(iface.types.empty()) << iface.name; // sanity
- if (std::none_of(iface.types.begin(), iface.types.end(), isAospAidlInterface)) continue;
- if (iface.stability != "vintf") continue;
+ for (const auto& treePackage : AidlInterfaceMetadata::all()) {
+ ASSERT_FALSE(treePackage.types.empty()) << treePackage.name;
+ if (std::none_of(treePackage.types.begin(), treePackage.types.end(),
+ isAospAidlInterface))
+ continue;
+ if (treePackage.stability != "vintf") continue;
- bool hasRegistration = false;
- bool knownMissing = false;
- for (const std::string& type : iface.types) {
- if (manifest.erase(type) > 0) hasRegistration = true;
- if (thoughtMissing.erase(getAidlPackage(type)) > 0) knownMissing = true;
+ // expect versions from 1 to latest version. If the package has development
+ // the latest version is the latest known version + 1. Each of these need
+ // to be checked for registration and knownMissing.
+ std::map<size_t, AidlPackageCheck> expectedVersions;
+ for (const auto version : treePackage.versions) {
+ expectedVersions[version] = {false, false};
+ }
+ if (treePackage.has_development) {
+ size_t version =
+ treePackage.versions.empty() ? 1 : *treePackage.versions.rbegin() + 1;
+ expectedVersions[version] = {false, false};
+ }
+
+ // Check all types and versions defined by the package for registration.
+ // The package version is considered registered if any of those types are
+ // present in the manifest with the same version.
+ // The package version is considered known missing if it is found in
+ // thoughtMissing.
+ bool latestRegistered = false;
+ for (const std::string& type : treePackage.types) {
+ for (auto& [version, check] : expectedVersions) {
+ if (manifest.erase({type, version}) > 0) {
+ if (version == expectedVersions.rbegin()->first) {
+ latestRegistered = true;
+ }
+ check.hasRegistration = true;
+ }
+ if (thoughtMissing.erase({getAidlPackage(type), version}) > 0)
+ check.knownMissing = true;
+ }
+ }
+
+ if (!latestRegistered && !expectedVersions.rbegin()->second.knownMissing) {
+ ADD_FAILURE() << "The latest version ("
+ << expectedVersions.rbegin()->first
+ << ") of the package is not implemented: "
+ << treePackage.name
+ << " which declares the following types:\n "
+ << base::Join(treePackage.types, "\n ");
+ }
+
+ for (const auto& [version, check] : expectedVersions) {
+ if (check.knownMissing) {
+ if (check.hasRegistration) {
+ ADD_FAILURE() << "Package in missing list, but available: "
+ << treePackage.name << " V" << version
+ << " which declares the following types:\n "
+ << base::Join(treePackage.types, "\n ");
}
- if (knownMissing) {
- if (hasRegistration) {
- ADD_FAILURE() << "Interface in missing list, but available: " << iface.name
- << " which declares the following types:\n "
- << base::Join(iface.types, "\n ");
- }
-
- continue;
- }
-
- EXPECT_TRUE(hasRegistration) << iface.name << " which declares the following types:\n "
- << base::Join(iface.types, "\n ");
+ continue;
+ }
}
+ }
- for (const std::string& iface : thoughtMissing) {
- ADD_FAILURE() << "Interface in manifest list and cannot find it anywhere: " << iface;
- }
+ for (const auto& package : thoughtMissing) {
+ ADD_FAILURE() << "Interface in missing list and cannot find it anywhere: "
+ << package.name << " V" << package.version;
+ }
- for (const std::string& iface : manifest) {
- ADD_FAILURE() << "Can't find manifest entry in tree: " << iface;
- }
+ for (const auto& package : manifest) {
+ ADD_FAILURE() << "Can't find manifest entry in tree: " << package.name
+ << " version: " << package.version;
+ }
}
diff --git a/tests/powerwash/src/com/android/cuttlefish/tests/PowerwashTest.java b/tests/powerwash/src/com/android/cuttlefish/tests/PowerwashTest.java
index 84fa852..42d9dda 100644
--- a/tests/powerwash/src/com/android/cuttlefish/tests/PowerwashTest.java
+++ b/tests/powerwash/src/com/android/cuttlefish/tests/PowerwashTest.java
@@ -15,15 +15,19 @@
*/
package com.android.cuttlefish.tests;
+import static org.junit.Assert.assertTrue;
+
import com.android.tradefed.device.cloud.RemoteAndroidVirtualDevice;
+import com.android.tradefed.device.internal.DeviceResetHandler;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import java.io.File;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
+
/**
* Test powerwash function.
*
@@ -48,14 +52,18 @@
if (file == null) {
Assert.fail("Setup failed: tmp file failed to persist after device reboot.");
}
-
+ boolean success = false;
if (getDevice() instanceof RemoteAndroidVirtualDevice) {
- ((RemoteAndroidVirtualDevice) getDevice()).powerwashGce();
+ success = ((RemoteAndroidVirtualDevice) getDevice()).powerwashGce();
} else {
- Assert.fail("This test only supports running in test lab setup.");
+ // We don't usually expect tests to use our feature server, but in this case we are
+ // validating the feature itself so it's fine
+ DeviceResetHandler handler = new DeviceResetHandler(getInvocationContext());
+ success = handler.resetDevice(getDevice());
}
+ assertTrue("Powerwash reset failed", success);
- // Verify that the device is back online and pre-xisting file is gone.
+ // Verify that the device is back online and pre-existing file is gone.
file = getDevice().pullFile(tmpFile);
if (file != null) {
Assert.fail("Powerwash failed: pre-existing file still exists.");
diff --git a/tests/ril/src/com/android/cuttlefish/ril/tests/RilE2eTests.java b/tests/ril/src/com/android/cuttlefish/ril/tests/RilE2eTests.java
index 3c5db3f..534f676 100644
--- a/tests/ril/src/com/android/cuttlefish/ril/tests/RilE2eTests.java
+++ b/tests/ril/src/com/android/cuttlefish/ril/tests/RilE2eTests.java
@@ -35,7 +35,6 @@
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -141,8 +140,6 @@
Assert.assertSame(TelephonyManager.DATA_CONNECTED, mTeleManager.getDataState());
}
- // See b/74256305
- @Ignore
@Test
public void testSignalLevels() throws Exception {
CellInfoGsm cellinfogsm = (CellInfoGsm)mTeleManager.getAllCellInfo().get(0);
diff --git a/tools/latest_fetch_cvd.sh b/tools/latest_fetch_cvd.sh
new file mode 100755
index 0000000..d853786
--- /dev/null
+++ b/tools/latest_fetch_cvd.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+LATEST_BUILD_ID=`curl "https://www.googleapis.com/android/internal/build/v3/builds?branch=aosp-master&buildAttemptStatus=complete&buildType=submitted&maxResults=1&successful=true&target=aosp_cf_x86_64_phone-userdebug" 2>/dev/null | \
+ python3 -c "import sys, json; print(json.load(sys.stdin)['builds'][0]['buildId'])"`
+LATEST_BUILD_URL=`curl "https://www.googleapis.com/android/internal/build/v3/builds/$LATEST_BUILD_ID/aosp_cf_x86_64_phone-userdebug/attempts/latest/artifacts/fetch_cvd/url" 2>/dev/null | \
+ python3 -c "import sys, json; print(json.load(sys.stdin)['signedUrl'])"`
+
+DOWNLOAD_TARGET=`mktemp`
+
+curl "${LATEST_BUILD_URL}" -o $DOWNLOAD_TARGET
+
+chmod +x $DOWNLOAD_TARGET
+
+exec $DOWNLOAD_TARGET $@
diff --git a/vsoc_arm64/BoardConfig.mk b/vsoc_arm64/BoardConfig.mk
index 048d8cd..bc016a8 100644
--- a/vsoc_arm64/BoardConfig.mk
+++ b/vsoc_arm64/BoardConfig.mk
@@ -20,9 +20,6 @@
-include device/google/cuttlefish/shared/BoardConfig.mk
-# GKI_VER is defined in kernel.mk, if not defined in the environment variable.
-BOARD_KERNEL_MODULE_INTERFACE_VERSIONS := $(GKI_VER)-android12-0
-
TARGET_BOARD_PLATFORM := vsoc_arm64
TARGET_ARCH := arm64
TARGET_ARCH_VARIANT := armv8-a
@@ -36,7 +33,7 @@
TARGET_TRANSLATE_2ND_ARCH := false
ifeq ($(BOARD_VENDOR_RAMDISK_KERNEL_MODULES),)
- BOARD_VENDOR_RAMDISK_KERNEL_MODULES += $(wildcard kernel/prebuilts/common-modules/virtual-device/$(GKI_VER)/arm64/*.ko)
+ BOARD_VENDOR_RAMDISK_KERNEL_MODULES += $(wildcard kernel/prebuilts/common-modules/virtual-device/$(TARGET_KERNEL_USE)/arm64/*.ko)
endif
HOST_CROSS_OS := linux_bionic
diff --git a/vsoc_arm64/bootloader.mk b/vsoc_arm64/bootloader.mk
index a5aea94..ce29443 100644
--- a/vsoc_arm64/bootloader.mk
+++ b/vsoc_arm64/bootloader.mk
@@ -18,5 +18,3 @@
# FIXME: Copying the QEMU bootloader for now, but this should be updated..
BOARD_PREBUILT_BOOTLOADER := \
device/google/cuttlefish_prebuilts/bootloader/crosvm_aarch64/u-boot.bin
-PRODUCT_COPY_FILES += \
- device/google/cuttlefish_prebuilts/bootloader/qemu_aarch64/u-boot.bin:bootloader.qemu
diff --git a/vsoc_arm64/kernel.mk b/vsoc_arm64/kernel.mk
index bcc15cf..c27f7cb 100644
--- a/vsoc_arm64/kernel.mk
+++ b/vsoc_arm64/kernel.mk
@@ -13,6 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-GKI_VER ?= 5.10
+TARGET_KERNEL_USE ?= 5.10
-PRODUCT_COPY_FILES += kernel/prebuilts/$(GKI_VER)/arm64/kernel-$(GKI_VER):kernel
+PRODUCT_COPY_FILES += kernel/prebuilts/$(TARGET_KERNEL_USE)/arm64/kernel-$(TARGET_KERNEL_USE):kernel
diff --git a/vsoc_arm64_only/phone/aosp_cf.mk b/vsoc_arm64_only/phone/aosp_cf.mk
index 6de27b6..5bcfc7b 100644
--- a/vsoc_arm64_only/phone/aosp_cf.mk
+++ b/vsoc_arm64_only/phone/aosp_cf.mk
@@ -55,7 +55,7 @@
PRODUCT_NAME := aosp_cf_arm64_only_phone
PRODUCT_DEVICE := vsoc_arm64_only
PRODUCT_MANUFACTURER := Google
-PRODUCT_MODEL := Cuttlefish arm64 phone (64-bit only)
+PRODUCT_MODEL := Cuttlefish arm64 phone 64-bit only
PRODUCT_VENDOR_PROPERTIES += \
ro.soc.manufacturer=$(PRODUCT_MANUFACTURER) \
diff --git a/vsoc_arm_only/bootloader.mk b/vsoc_arm_only/bootloader.mk
index 93de14e..959cd61 100644
--- a/vsoc_arm_only/bootloader.mk
+++ b/vsoc_arm_only/bootloader.mk
@@ -18,5 +18,3 @@
# FIXME: Copying the QEMU bootloader for now, but this should be updated..
BOARD_PREBUILT_BOOTLOADER := \
device/google/cuttlefish_prebuilts/bootloader/qemu_arm/u-boot.bin
-PRODUCT_COPY_FILES += \
- device/google/cuttlefish_prebuilts/bootloader/qemu_arm/u-boot.bin:bootloader.qemu
diff --git a/vsoc_arm_only/phone/aosp_cf.mk b/vsoc_arm_only/phone/aosp_cf.mk
index e0875bf..2643c90 100644
--- a/vsoc_arm_only/phone/aosp_cf.mk
+++ b/vsoc_arm_only/phone/aosp_cf.mk
@@ -40,6 +40,7 @@
$(call inherit-product, $(SRC_TARGET_DIR)/product/go_defaults_512.mk)
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
system/apex/com.android.tethering.inprocess.apex \
+ system/apex/com.android.tethering.inprocess.capex \
system/app/PlatformCaptivePortalLogin/PlatformCaptivePortalLogin.apk \
system/priv-app/CellBroadcastServiceModulePlatform/CellBroadcastServiceModulePlatform.apk \
system/priv-app/InProcessNetworkStack/InProcessNetworkStack.apk \
@@ -63,7 +64,7 @@
PRODUCT_NAME := aosp_cf_arm_only_phone
PRODUCT_DEVICE := vsoc_arm_only
PRODUCT_MANUFACTURER := Google
-PRODUCT_MODEL := Cuttlefish arm phone (32-bit only)
+PRODUCT_MODEL := Cuttlefish arm phone 32-bit only
PRODUCT_VENDOR_PROPERTIES += \
ro.soc.manufacturer=$(PRODUCT_MANUFACTURER) \
diff --git a/vsoc_x86/auto/audio_policy_configuration.xml b/vsoc_x86/auto/audio_policy_configuration.xml
new file mode 100644
index 0000000..93d4130
--- /dev/null
+++ b/vsoc_x86/auto/audio_policy_configuration.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2021, 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.
+*/
+-->
+
+<!--
+ Overlay resources to configure car service based on each OEM's preference.
+ See also packages/services/Car/service/res/values/config.xml
+-->
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!-- Global configuration Decalaration -->
+ <globalConfiguration speaker_drc_enabled="true"/>
+
+ <module name="primary" halVersion="2.0">
+ <attachedDevices>
+ <item>Speaker</item>
+ <item>Built-In Mic</item>
+ </attachedDevices>
+ <defaultOutputDevice>Speaker</defaultOutputDevice>
+ <mixPorts>
+ <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="primary input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="Speaker">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+ </gains>
+ </devicePort>
+
+ <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+ </devicePort>
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Speaker"
+ sources="primary output"/>
+ <route type="mix" sink="primary input"
+ sources="Built-In Mic"/>
+ </routes>
+ </module>
+
+ <xi:include href="audio_policy_volumes.xml"/>
+ <xi:include href="default_volume_tables.xml"/>
+
+ <!-- End of Volume section -->
+ <!-- End of Modules section -->
+
+</audioPolicyConfiguration>
\ No newline at end of file
diff --git a/vsoc_x86/auto/car_audio_configuration.xml b/vsoc_x86/auto/car_audio_configuration.xml
new file mode 100644
index 0000000..53ca217
--- /dev/null
+++ b/vsoc_x86/auto/car_audio_configuration.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<!--
+ Defines the audio configuration in a car, including
+ - Audio zones
+ - Context to audio bus mappings
+ - Volume groups
+ in the car environment.
+-->
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true">
+ <volumeGroups>
+ <group>
+ <device address="Speaker">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ <context context="emergency"/>
+ <context context="safety"/>
+ <context context="vehicle_status"/>
+ <context context="announcement"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/vsoc_x86/auto/overlay/frameworks/base/core/res/res/values/config.xml b/vsoc_x86/auto/overlay/frameworks/base/core/res/res/values/config.xml
new file mode 100644
index 0000000..ca5f91e
--- /dev/null
+++ b/vsoc_x86/auto/overlay/frameworks/base/core/res/res/values/config.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2021, 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.
+*/
+-->
+
+<!--
+ Overlay resources to configure car service based on each OEM's preference.
+ See also packages/services/Car/service/res/values/config.xml
+-->
+<resources>
+ <!-- Car uses hardware amplifier for volume. -->
+ <bool name="config_useFixedVolume">true</bool>
+ <!--
+ Handle volume keys directly in CarAudioService without passing them to the foreground app
+ -->
+ <bool name="config_handleVolumeKeysInWindowManager">true</bool>
+</resources>
diff --git a/vsoc_x86/auto/overlay/packages/services/Car/service/res/values/config.xml b/vsoc_x86/auto/overlay/packages/services/Car/service/res/values/config.xml
new file mode 100644
index 0000000..f1585a2
--- /dev/null
+++ b/vsoc_x86/auto/overlay/packages/services/Car/service/res/values/config.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2021, 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.
+*/
+-->
+
+<!--
+ Overlay resources to configure car service based on each OEM's preference.
+ See also packages/services/Car/service/res/values/config.xml
+-->
+<resources>
+ <bool name="audioUseDynamicRouting">true</bool>
+ <!-- Configuration to enable muting of individual volume groups. If this is set to
+ false, muting of individual volume groups is disabled, instead muting will toggle master
+ mute. If this is set to true, car volume group muting is enabled and each individual
+ volume group can be muted separately. -->
+ <bool name="audioUseCarVolumeGroupMuting">false</bool>
+ <!-- Configuration to enable IAudioControl#onDevicesToDuckChange API to inform HAL when to
+ duck. If this is set to true, the API will receive signals indicating which output devices
+ to duck as well as what usages are currently holding focus. If set to false, the API will
+ not be called. -->
+ <bool name="audioUseHalDuckingSignals">false</bool>
+</resources>
diff --git a/vsoc_x86/go_512_phone/device.mk b/vsoc_x86/go_512_phone/device.mk
index 2df9023..1a34001 100644
--- a/vsoc_x86/go_512_phone/device.mk
+++ b/vsoc_x86/go_512_phone/device.mk
@@ -16,6 +16,7 @@
$(call inherit-product, device/google/cuttlefish/shared/go_512/device.mk)
$(call inherit-product, device/google/cuttlefish/vsoc_x86_64/kernel.mk)
+$(call inherit-product, device/google/cuttlefish/vsoc_x86_64/bootloader.mk)
PRODUCT_NAME := aosp_cf_x86_go_512_phone
PRODUCT_DEVICE := vsoc_x86
diff --git a/vsoc_x86/go_phone/device.mk b/vsoc_x86/go_phone/device.mk
index 8b2a8f3..6fe021b 100644
--- a/vsoc_x86/go_phone/device.mk
+++ b/vsoc_x86/go_phone/device.mk
@@ -16,6 +16,7 @@
$(call inherit-product, device/google/cuttlefish/shared/go/device.mk)
$(call inherit-product, device/google/cuttlefish/vsoc_x86_64/kernel.mk)
+$(call inherit-product, device/google/cuttlefish/vsoc_x86_64/bootloader.mk)
PRODUCT_NAME := aosp_cf_x86_go_phone
PRODUCT_DEVICE := vsoc_x86
diff --git a/vsoc_x86_64/BoardConfig.mk b/vsoc_x86_64/BoardConfig.mk
index 7e3d927..52dde5c 100644
--- a/vsoc_x86_64/BoardConfig.mk
+++ b/vsoc_x86_64/BoardConfig.mk
@@ -20,9 +20,6 @@
-include device/google/cuttlefish/shared/BoardConfig.mk
-# GKI_VER is defined in kernel.mk, if not defined in the environment variable.
-BOARD_KERNEL_MODULE_INTERFACE_VERSIONS := $(GKI_VER)-android12-0
-
TARGET_BOARD_PLATFORM := vsoc_x86_64
TARGET_ARCH := x86_64
TARGET_ARCH_VARIANT := silvermont
@@ -46,5 +43,5 @@
BUILD_BROKEN_DUP_RULES := true
ifeq ($(BOARD_VENDOR_RAMDISK_KERNEL_MODULES),)
- BOARD_VENDOR_RAMDISK_KERNEL_MODULES += $(wildcard kernel/prebuilts/common-modules/virtual-device/$(GKI_VER)/x86-64/*.ko)
+ BOARD_VENDOR_RAMDISK_KERNEL_MODULES += $(wildcard kernel/prebuilts/common-modules/virtual-device/$(TARGET_KERNEL_USE)/x86-64/*.ko)
endif
diff --git a/vsoc_x86_64/bootloader.mk b/vsoc_x86_64/bootloader.mk
index b0d8c5d..6294ca7 100644
--- a/vsoc_x86_64/bootloader.mk
+++ b/vsoc_x86_64/bootloader.mk
@@ -17,5 +17,3 @@
TARGET_NO_BOOTLOADER := false
BOARD_PREBUILT_BOOTLOADER := \
device/google/cuttlefish_prebuilts/bootloader/crosvm_x86_64/u-boot.rom
-PRODUCT_COPY_FILES += \
- device/google/cuttlefish_prebuilts/bootloader/qemu_x86_64/u-boot.rom:bootloader.qemu
diff --git a/vsoc_x86_64/kernel.mk b/vsoc_x86_64/kernel.mk
index dccbeda..5dded66 100644
--- a/vsoc_x86_64/kernel.mk
+++ b/vsoc_x86_64/kernel.mk
@@ -13,6 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-GKI_VER ?= 5.10
+TARGET_KERNEL_USE ?= 5.10
-PRODUCT_COPY_FILES += kernel/prebuilts/$(GKI_VER)/x86_64/kernel-$(GKI_VER):kernel
+PRODUCT_COPY_FILES += kernel/prebuilts/$(TARGET_KERNEL_USE)/x86_64/kernel-$(TARGET_KERNEL_USE):kernel
diff --git a/vsoc_x86_64_only/phone/aosp_cf.mk b/vsoc_x86_64_only/phone/aosp_cf.mk
index 9596d5a..0e8757d 100644
--- a/vsoc_x86_64_only/phone/aosp_cf.mk
+++ b/vsoc_x86_64_only/phone/aosp_cf.mk
@@ -55,7 +55,7 @@
PRODUCT_NAME := aosp_cf_x86_64_only_phone
PRODUCT_DEVICE := vsoc_x86_64_only
PRODUCT_MANUFACTURER := Google
-PRODUCT_MODEL := Cuttlefish x86_64 phone (64-bit only)
+PRODUCT_MODEL := Cuttlefish x86_64 phone 64-bit only
PRODUCT_VENDOR_PROPERTIES += \
ro.soc.manufacturer=$(PRODUCT_MANUFACTURER) \
diff --git a/vsoc_x86_noapex/aosp_cf_noapex.mk b/vsoc_x86_noapex/aosp_cf_noapex.mk
index aa5d3b2..dd5d642 100644
--- a/vsoc_x86_noapex/aosp_cf_noapex.mk
+++ b/vsoc_x86_noapex/aosp_cf_noapex.mk
@@ -25,7 +25,3 @@
PRODUCT_DEVICE := vsoc_x86_noapex
PRODUCT_MANUFACTURER := Google
PRODUCT_MODEL := Cuttlefish x86 phone without APEX support
-
-PRODUCT_VENDOR_PROPERTIES += \
- ro.soc.manufacturer=$(PRODUCT_MANUFACTURER) \
- ro.soc.model=$(PRODUCT_DEVICE)
diff --git a/vsoc_x86_only/BoardConfig.mk b/vsoc_x86_only/BoardConfig.mk
index ec97f2d..8703a10 100644
--- a/vsoc_x86_only/BoardConfig.mk
+++ b/vsoc_x86_only/BoardConfig.mk
@@ -20,12 +20,9 @@
-include device/google/cuttlefish/shared/BoardConfig.mk
-# GKI_VER is defined in kernel.mk, if not defined in the environment variable.
-BOARD_KERNEL_MODULE_INTERFACE_VERSIONS := $(GKI_VER)-android12-0
-
TARGET_BOARD_PLATFORM := vsoc_x86
TARGET_ARCH := x86
TARGET_ARCH_VARIANT := x86
TARGET_CPU_ABI := x86
-BOARD_VENDOR_RAMDISK_KERNEL_MODULES += $(wildcard device/google/cuttlefish_prebuilts/kernel/$(GKI_VER)-i686/*.ko)
+BOARD_VENDOR_RAMDISK_KERNEL_MODULES += $(wildcard device/google/cuttlefish_prebuilts/kernel/$(TARGET_KERNEL_USE)-i686/*.ko)
diff --git a/vsoc_x86_only/kernel.mk b/vsoc_x86_only/kernel.mk
index e6d4924..23cf086 100644
--- a/vsoc_x86_only/kernel.mk
+++ b/vsoc_x86_only/kernel.mk
@@ -13,6 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-GKI_VER ?= 5.10
+TARGET_KERNEL_USE ?= 5.10
-PRODUCT_COPY_FILES += device/google/cuttlefish_prebuilts/kernel/$(GKI_VER)-i686/kernel-$(GKI_VER):kernel
+PRODUCT_COPY_FILES += device/google/cuttlefish_prebuilts/kernel/$(TARGET_KERNEL_USE)-i686/kernel-$(TARGET_KERNEL_USE):kernel
diff --git a/vsoc_x86_only/phone/aosp_cf.mk b/vsoc_x86_only/phone/aosp_cf.mk
index 17324d4..1aacd16 100644
--- a/vsoc_x86_only/phone/aosp_cf.mk
+++ b/vsoc_x86_only/phone/aosp_cf.mk
@@ -53,7 +53,7 @@
PRODUCT_NAME := aosp_cf_x86_only_phone
PRODUCT_DEVICE := vsoc_x86_only
PRODUCT_MANUFACTURER := Google
-PRODUCT_MODEL := Cuttlefish x86 phone (32-bit kernel)
+PRODUCT_MODEL := Cuttlefish x86 phone 32-bit kernel
PRODUCT_VENDOR_PROPERTIES += \
ro.soc.manufacturer=$(PRODUCT_MANUFACTURER) \