Snap for 6533464 from f1aef0d73ab38e5304694d9aefde87acdf77753f to sdk-release
Change-Id: If77bfde4bb3516ca6c36c899d702a854d224d1f0
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/common/libs/device_config/host_device_config.cpp b/common/libs/device_config/host_device_config.cpp
index f1399b2..ba34128 100644
--- a/common/libs/device_config/host_device_config.cpp
+++ b/common/libs/device_config/host_device_config.cpp
@@ -41,22 +41,25 @@
uint8_t ril_prefixlen = -1;
std::string ril_ipaddr;
std::string ril_gateway;
- std::string ril_dns = "8.8.8.8";
+ std::string ril_dns;
std::string ril_broadcast;
- bool ObtainConfig(const std::string& interface) {
- bool ret = ParseIntefaceAttributes(interface);
- LOG(INFO) << "Network config:";
- LOG(INFO) << "ipaddr = " << ril_ipaddr;
- LOG(INFO) << "gateway = " << ril_gateway;
- LOG(INFO) << "dns = " << ril_dns;
- LOG(INFO) << "broadcast = " << ril_broadcast;
- LOG(INFO) << "prefix length = " << static_cast<int>(ril_prefixlen);
+ bool ObtainConfig(const std::string& interface, const std::string& dns) {
+ bool ret = ParseInterfaceAttributes(interface);
+ if (ret) {
+ ril_dns = dns;
+ LOG(INFO) << "Network config:";
+ LOG(INFO) << "ipaddr = " << ril_ipaddr;
+ LOG(INFO) << "gateway = " << ril_gateway;
+ LOG(INFO) << "dns = " << ril_dns;
+ LOG(INFO) << "broadcast = " << ril_broadcast;
+ LOG(INFO) << "prefix length = " << static_cast<int>(ril_prefixlen);
+ }
return ret;
}
private:
- bool ParseIntefaceAttributes(struct ifaddrs* ifa) {
+ bool ParseInterfaceAttributes(struct ifaddrs* ifa) {
struct sockaddr_in* sa;
char* addr_str;
@@ -72,6 +75,16 @@
this->ril_broadcast = strtok(addr_str, "\n");
auto broadcast_s_addr = ntohl(sa->sin_addr.s_addr);
+ // Detect misconfigured network interfaces. All network interfaces must
+ // have a valid broadcast address set; if there is none set, glibc may
+ // return the interface address in the broadcast field. This causes
+ // no packets to be routed correctly from the guest.
+ if (this->ril_gateway == this->ril_broadcast) {
+ LOG(ERROR) << "Gateway and Broadcast addresses are the same on "
+ << ifa->ifa_name << ", which is invalid.";
+ return false;
+ }
+
// Netmask
sa = reinterpret_cast<sockaddr_in*>(ifa->ifa_netmask);
this->ril_prefixlen = number_of_ones(sa->sin_addr.s_addr);
@@ -99,14 +112,14 @@
return true;
}
- bool ParseIntefaceAttributes(const std::string& interface) {
+ bool ParseInterfaceAttributes(const std::string& interface) {
struct ifaddrs *ifa_list{}, *ifa{};
bool ret = false;
getifaddrs(&ifa_list);
for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
if (strcmp(ifa->ifa_name, interface.c_str()) == 0 &&
ifa->ifa_addr->sa_family == AF_INET) {
- ret = ParseIntefaceAttributes(ifa);
+ ret = ParseInterfaceAttributes(ifa);
break;
}
}
@@ -140,9 +153,16 @@
const vsoc::CuttlefishConfig& config) {
auto instance = config.ForDefaultInstance();
NetConfig netconfig;
- if (!netconfig.ObtainConfig(instance.mobile_bridge_name())) {
- LOG(ERROR) << "Unable to obtain the network configuration";
- return false;
+ // Check the mobile bridge first; this was the traditional way we configured
+ // the mobile interface. If that fails, it probably means we are using a
+ // newer version of cuttlefish-common, and we can use the tap device
+ // directly instead.
+ if (!netconfig.ObtainConfig(instance.mobile_bridge_name(),
+ config.ril_dns())) {
+ if (!netconfig.ObtainConfig(instance.mobile_tap_name(), config.ril_dns())) {
+ LOG(ERROR) << "Unable to obtain the network configuration";
+ return false;
+ }
}
auto res = snprintf(data_.ril.ipaddr, sizeof(data_.ril.ipaddr), "%s",
diff --git a/common/libs/fs/shared_buf.cc b/common/libs/fs/shared_buf.cc
index dbf33ba..5ae1aa2 100644
--- a/common/libs/fs/shared_buf.cc
+++ b/common/libs/fs/shared_buf.cc
@@ -22,13 +22,15 @@
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
-namespace cvd {
-
namespace {
const size_t BUFF_SIZE = 1 << 14;
-static ssize_t WriteAll(SharedFD fd, const char* buf, size_t size) {
+} // namespace
+
+namespace cvd {
+
+ssize_t WriteAll(SharedFD fd, const char* buf, size_t size) {
size_t total_written = 0;
ssize_t written = 0;
while ((written = fd->Write((void*)&(buf[total_written]), size - total_written)) > 0) {
@@ -60,8 +62,6 @@
return total_read;
}
-} // namespace
-
ssize_t ReadAll(SharedFD fd, std::string* buf) {
char buff[BUFF_SIZE];
std::stringstream ss;
diff --git a/common/libs/fs/shared_buf.h b/common/libs/fs/shared_buf.h
index c6e7590..f8f86b7 100644
--- a/common/libs/fs/shared_buf.h
+++ b/common/libs/fs/shared_buf.h
@@ -53,6 +53,29 @@
ssize_t ReadExact(SharedFD fd, std::vector<char>* buf);
/**
+ * Reads from fd until reading `size` bytes or errors.
+ *
+ * On a successful read, returns buf->size().
+ *
+ * If a read error is encountered, returns -1. buf will contain any data read
+ * up until that point and errno will be set.
+ */
+ssize_t ReadExact(SharedFD fd, char* buf, size_t size);
+
+/*
+ * Reads from fd until reading `sizeof(T)` bytes or errors.
+ *
+ * On a successful read, returns `sizeof(T)`.
+ *
+ * If a read error is encountered, returns -1. buf will contain any data read
+ * up until that point and errno will be set.
+ */
+template<typename T>
+ssize_t ReadExactBinary(SharedFD fd, T* binary_data) {
+ return ReadExact(fd, (char*) binary_data, sizeof(*binary_data));
+}
+
+/**
* Writes to fd until writing all bytes in buf.
*
* On a successful write, returns buf.size().
@@ -72,4 +95,27 @@
*/
ssize_t WriteAll(SharedFD fd, const std::vector<char>& buf);
+/**
+ * Writes to fd until `size` bytes are written from `buf`.
+ *
+ * On a successful write, returns `size`.
+ *
+ * If a write error is encountered, returns -1. Some data may have already been
+ * written to fd at that point.
+ */
+ssize_t WriteAll(SharedFD fd, const char* buf, size_t size);
+
+/**
+ * Writes to fd until `sizeof(T)` bytes are written from binary_data.
+ *
+ * On a successful write, returns `sizeof(T)`.
+ *
+ * If a write error is encountered, returns -1. Some data may have already been
+ * written to fd at that point.
+ */
+template<typename T>
+ssize_t WriteAllBinary(SharedFD fd, const T* binary_data) {
+ return WriteAll(fd, (const char*) binary_data, sizeof(*binary_data));
+}
+
} // namespace cvd
diff --git a/common/libs/security/Android.bp b/common/libs/security/Android.bp
new file mode 100644
index 0000000..c007c35
--- /dev/null
+++ b/common/libs/security/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+
+cc_library {
+ name: "libcuttlefish_security",
+ defaults: ["hidl_defaults", "cuttlefish_host_and_guest"],
+ srcs: [
+ "keymaster_channel.cpp",
+ ],
+ header_libs: [
+ "libhardware_headers",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcuttlefish_fs",
+ "libkeymaster_messages",
+ "liblog",
+ ],
+}
diff --git a/common/libs/security/keymaster_channel.cpp b/common/libs/security/keymaster_channel.cpp
new file mode 100644
index 0000000..34c7da6
--- /dev/null
+++ b/common/libs/security/keymaster_channel.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 "keymaster_channel.h"
+
+#include <android-base/logging.h>
+#include "keymaster/android_keymaster_utils.h"
+
+#include "common/libs/fs/shared_buf.h"
+
+namespace cvd {
+
+ManagedKeymasterMessage CreateKeymasterMessage(
+ AndroidKeymasterCommand command, bool is_response, size_t payload_size) {
+ auto memory = new uint8_t[payload_size + sizeof(keymaster_message)];
+ auto message = reinterpret_cast<keymaster_message*>(memory);
+ message->cmd = command;
+ message->is_response = is_response;
+ message->payload_size = payload_size;
+ return ManagedKeymasterMessage(message);
+}
+
+void KeymasterCommandDestroyer::operator()(keymaster_message* ptr) {
+ {
+ keymaster::Eraser(ptr, sizeof(keymaster_message) + ptr->payload_size);
+ }
+ delete reinterpret_cast<uint8_t*>(ptr);
+}
+
+KeymasterChannel::KeymasterChannel(SharedFD channel) : channel_(channel) {
+}
+
+bool KeymasterChannel::SendRequest(
+ AndroidKeymasterCommand command, const keymaster::Serializable& message) {
+ return SendMessage(command, false, message);
+}
+
+bool KeymasterChannel::SendResponse(
+ AndroidKeymasterCommand command, const keymaster::Serializable& message) {
+ return SendMessage(command, true, message);
+}
+
+bool KeymasterChannel::SendMessage(
+ AndroidKeymasterCommand command,
+ bool is_response,
+ const keymaster::Serializable& message) {
+ LOG(DEBUG) << "Sending message with id: " << command;
+ auto payload_size = message.SerializedSize();
+ auto to_send = CreateKeymasterMessage(command, is_response, payload_size);
+ message.Serialize(to_send->payload, to_send->payload + payload_size);
+ auto write_size = payload_size + sizeof(keymaster_message);
+ auto to_send_bytes = reinterpret_cast<const char*>(to_send.get());
+ auto written = cvd::WriteAll(channel_, to_send_bytes, write_size);
+ if (written == -1) {
+ LOG(ERROR) << "Could not write Keymaster Message: " << channel_->StrError();
+ }
+ return written == write_size;
+}
+
+ManagedKeymasterMessage KeymasterChannel::ReceiveMessage() {
+ struct keymaster_message message_header;
+ auto read = cvd::ReadExactBinary(channel_, &message_header);
+ if (read != sizeof(keymaster_message)) {
+ LOG(ERROR) << "Expected " << sizeof(keymaster_message) << ", received "
+ << read;
+ LOG(ERROR) << "Could not read Keymaster Message: " << channel_->StrError();
+ return {};
+ }
+ LOG(DEBUG) << "Received message with id: " << message_header.cmd;
+ auto message = CreateKeymasterMessage(message_header.cmd,
+ message_header.is_response,
+ message_header.payload_size);
+ auto message_bytes = reinterpret_cast<char*>(message->payload);
+ read = cvd::ReadExact(channel_, message_bytes, message->payload_size);
+ if (read != message->payload_size) {
+ LOG(ERROR) << "Could not read Keymaster Message: " << channel_->StrError();
+ return {};
+ }
+ return message;
+}
+
+}
diff --git a/common/libs/security/keymaster_channel.h b/common/libs/security/keymaster_channel.h
new file mode 100644
index 0000000..529325a
--- /dev/null
+++ b/common/libs/security/keymaster_channel.h
@@ -0,0 +1,86 @@
+/*
+ * 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 "keymaster/android_keymaster_messages.h"
+#include "keymaster/serializable.h"
+
+#include "common/libs/fs/shared_fd.h"
+
+#include <memory>
+
+namespace keymaster {
+
+/**
+ * keymaster_message - Serial header for communicating with KM server
+ * @cmd: the command, one of AndroidKeymasterCommand.
+ * @payload: start of the serialized command specific payload
+ */
+struct keymaster_message {
+ AndroidKeymasterCommand cmd : 31;
+ bool is_response : 1;
+ uint32_t payload_size;
+ uint8_t payload[0];
+};
+
+} // namespace keymaster
+
+namespace cvd {
+
+using keymaster::AndroidKeymasterCommand;
+using keymaster::keymaster_message;
+
+/**
+ * A destroyer for keymaster_message instances created with
+ * CreateKeymasterMessage. Wipes memory from the keymaster_message instances.
+ */
+class KeymasterCommandDestroyer {
+public:
+ void operator()(keymaster_message* ptr);
+};
+
+/** An owning pointer for a keymaster_message instance. */
+using ManagedKeymasterMessage =
+ std::unique_ptr<keymaster_message, KeymasterCommandDestroyer>;
+
+/**
+ * Allocates memory for a keymaster_message carrying a message of size
+ * `payload_size`.
+ */
+ManagedKeymasterMessage CreateKeymasterMessage(
+ AndroidKeymasterCommand command, bool is_response, size_t payload_size);
+
+/*
+ * Interface for communication channels that synchronously communicate Keymaster
+ * IPC/RPC calls. Sends messages over a file descriptor.
+ */
+class KeymasterChannel {
+private:
+ SharedFD channel_;
+ bool SendMessage(AndroidKeymasterCommand command, bool response,
+ const keymaster::Serializable& message);
+public:
+ KeymasterChannel(SharedFD channel);
+
+ bool SendRequest(AndroidKeymasterCommand command,
+ const keymaster::Serializable& message);
+ bool SendResponse(AndroidKeymasterCommand command,
+ const keymaster::Serializable& message);
+ ManagedKeymasterMessage ReceiveMessage();
+};
+
+} // namespace cvd
diff --git a/common/libs/utils/Android.bp b/common/libs/utils/Android.bp
index a3fc45e..830a9e9 100644
--- a/common/libs/utils/Android.bp
+++ b/common/libs/utils/Android.bp
@@ -23,11 +23,13 @@
"files.cpp",
"users.cpp",
"network.cpp",
+ "base64.cpp",
],
shared: {
shared_libs: [
"libbase",
"libcuttlefish_fs",
+ "libcrypto",
],
},
static: {
@@ -35,6 +37,9 @@
"libbase",
"libcuttlefish_fs",
],
+ shared_libs: [
+ "libcrypto", // libcrypto_static is not accessible from all targets
+ ],
},
defaults: ["cuttlefish_host_and_guest"],
}
diff --git a/common/libs/utils/base64.cpp b/common/libs/utils/base64.cpp
new file mode 100644
index 0000000..cd4fbb7
--- /dev/null
+++ b/common/libs/utils/base64.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "common/libs/utils/base64.h"
+
+#include <openssl/base64.h>
+
+namespace cvd {
+
+bool EncodeBase64(const void *data, size_t size, std::string *out) {
+ size_t enc_len = 0;
+ auto len_res = EVP_EncodedLength(&enc_len, size);
+ if (!len_res) {
+ return false;
+ }
+ out->resize(enc_len);
+ auto enc_res = EVP_EncodeBlock(reinterpret_cast<uint8_t *>(out->data()),
+ reinterpret_cast<const uint8_t *>(data), size);
+ if (enc_res < 0) {
+ return false;
+ }
+ out->resize(enc_res); // Don't count the terminating \0 character
+ return true;
+}
+
+bool DecodeBase64(const std::string &data, std::vector<uint8_t> *buffer) {
+ size_t out_len;
+ auto len_res = EVP_DecodedLength(&out_len, data.size());
+ if (!len_res) {
+ return false;
+ }
+ buffer->resize(out_len);
+ return EVP_DecodeBase64(buffer->data(), &out_len, out_len,
+ reinterpret_cast<const uint8_t *>(data.data()),
+ data.size());
+}
+
+} // namespace cvd
diff --git a/common/libs/utils/base64.h b/common/libs/utils/base64.h
new file mode 100644
index 0000000..15ad268
--- /dev/null
+++ b/common/libs/utils/base64.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <cinttypes>
+#include <string>
+#include <vector>
+
+namespace cvd {
+
+bool EncodeBase64(const void* _data, size_t size, std::string* out);
+
+bool DecodeBase64(const std::string& data, std::vector<uint8_t>* buffer);
+
+} // namespace cvd
diff --git a/common/libs/utils/files.cpp b/common/libs/utils/files.cpp
index 24d0223..ac81346 100644
--- a/common/libs/utils/files.cpp
+++ b/common/libs/utils/files.cpp
@@ -22,6 +22,7 @@
#include <climits>
#include <cstdio>
#include <cstdlib>
+#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -107,11 +108,33 @@
return std::chrono::system_clock::time_point(seconds);
}
+bool RenameFile(const std::string& old_name, const std::string& new_name) {
+ LOG(INFO) << "Renaming " << old_name << " to " << new_name;
+ if(rename(old_name.c_str(), new_name.c_str())) {
+ LOG(ERROR) << "File rename failed due to " << strerror(errno);
+ return false;
+ }
+
+ return true;
+}
+
bool RemoveFile(const std::string& file) {
LOG(INFO) << "Removing " << file;
return remove(file.c_str()) == 0;
}
+
+std::string ReadFile(const std::string& file) {
+ std::string contents;
+ std::ifstream in(file, std::ios::in | std::ios::binary);
+ in.seekg(0, std::ios::end);
+ contents.resize(in.tellg());
+ in.seekg(0, std::ios::beg);
+ in.read(&contents[0], contents.size());
+ in.close();
+ return(contents);
+}
+
std::string CurrentDirectory() {
char* path = getcwd(nullptr, 0);
std::string ret(path);
diff --git a/common/libs/utils/files.h b/common/libs/utils/files.h
index 13e6892..0541344 100644
--- a/common/libs/utils/files.h
+++ b/common/libs/utils/files.h
@@ -27,6 +27,8 @@
bool IsDirectoryEmpty(const std::string& path);
off_t FileSize(const std::string& path);
bool RemoveFile(const std::string& file);
+bool RenameFile(const std::string& old_name, const std::string& new_name);
+std::string ReadFile(const std::string& file);
std::chrono::system_clock::time_point FileModificationTime(const std::string& path);
// The returned value may contain .. or . if these are present in the path
diff --git a/common/libs/utils/network.cpp b/common/libs/utils/network.cpp
index 033430e..33a1ea9 100644
--- a/common/libs/utils/network.cpp
+++ b/common/libs/utils/network.cpp
@@ -30,10 +30,19 @@
#include "android-base/logging.h"
#include "common/libs/fs/shared_buf.h"
+#include "common/libs/utils/environment.h"
#include "common/libs/utils/subprocess.h"
namespace cvd {
namespace {
+
+static std::string DefaultHostArtifactsPath(const std::string& file_name) {
+ return (cvd::StringFromEnv("ANDROID_HOST_OUT",
+ cvd::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).
@@ -88,28 +97,41 @@
return tap_fd;
}
- struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
- strncpy(ifr.ifr_name, interface_name.c_str(), IFNAMSIZ);
+ if (cvd::HostArch() == "aarch64") {
+ auto tapsetiff_path = DefaultHostArtifactsPath("bin/tapsetiff");
+ cvd::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 cvd::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);
- int err = tap_fd->Ioctl(TUNSETIFF, &ifr);
- if (err < 0) {
- LOG(ERROR) << "Unable to connect to " << interface_name
- << " tap interface: " << tap_fd->StrError();
- tap_fd->Close();
- return cvd::SharedFD();
- }
+ int err = tap_fd->Ioctl(TUNSETIFF, &ifr);
+ if (err < 0) {
+ LOG(ERROR) << "Unable to connect to " << interface_name
+ << " tap interface: " << tap_fd->StrError();
+ tap_fd->Close();
+ return cvd::SharedFD();
+ }
- // The interface's configuration may have been modified or just not set
- // correctly on creation. While qemu checks this and enforces the right
- // configuration, crosvm does not, so it needs to be set before it's passed to
- // it.
- tap_fd->Ioctl(TUNSETOFFLOAD,
- reinterpret_cast<void*>(TUN_F_CSUM | TUN_F_UFO | TUN_F_TSO4 |
+ // 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 len = SIZE_OF_VIRTIO_NET_HDR_V1;
+ tap_fd->Ioctl(TUNSETVNETHDRSZ, &len);
+ }
return tap_fd;
}
diff --git a/common/libs/utils/users.cpp b/common/libs/utils/users.cpp
index cd38982..0b59069 100644
--- a/common/libs/utils/users.cpp
+++ b/common/libs/utils/users.cpp
@@ -45,7 +45,7 @@
if (grp_p != nullptr) {
return grp.gr_gid;
} else {
- LOG(ERROR) << "Group " << group_name << " does not exist";
+ // Caller may be checking with non-existent group name
return -1;
}
} else {
@@ -89,4 +89,4 @@
return true;
}
return false;
-}
\ No newline at end of file
+}
diff --git a/guest/commands/vtpm_manager/main.cpp b/guest/commands/vtpm_manager/main.cpp
index f2df360..d2b3584 100644
--- a/guest/commands/vtpm_manager/main.cpp
+++ b/guest/commands/vtpm_manager/main.cpp
@@ -44,32 +44,39 @@
bool ReadResponseLoop(cvd::SharedFD in_fd, cvd::SharedFD out_fd) {
std::vector<char> message;
while (true) {
- std::vector<char> response_size_bytes(4, 0);
- CHECK(cvd::ReadExact(in_fd, &response_size_bytes) == 4) << "Could not read response size";
+ std::uint32_t response_size;
+ CHECK(cvd::ReadExactBinary(in_fd, &response_size) == 4)
+ << "Could not read response size";
// the tpm simulator writes 4 extra bytes at the end of the message.
- std::uint32_t response_size = be32toh(*reinterpret_cast<std::uint32_t*>(response_size_bytes.data()));
+ response_size = be32toh(response_size);
message.resize(response_size, '\0');
- CHECK(cvd::ReadExact(in_fd, &message) == response_size) << "Could not read response message";
+ CHECK(cvd::ReadExact(in_fd, &message) == response_size)
+ << "Could not read response message";
auto header = reinterpret_cast<tpm_message_header*>(message.data());
auto host_rc = betoh32(header->ordinal);
- LOG(DEBUG) << "TPM response was: \"" << Tss2_RC_Decode(host_rc) << "\" (" << host_rc << ")";
+ LOG(DEBUG) << "TPM response was: \"" << Tss2_RC_Decode(host_rc) << "\" ("
+ << host_rc << ")";
std::vector<char> response_bytes(4, 0);
- CHECK(cvd::ReadExact(in_fd, &response_bytes) == 4) << "Could not read parity response";
- CHECK(cvd::WriteAll(out_fd, message) == message.size()) << "Could not forward message to vTPM";
+ CHECK(cvd::ReadExact(in_fd, &response_bytes) == 4)
+ << "Could not read parity response";
+ CHECK(cvd::WriteAll(out_fd, message) == message.size())
+ << "Could not forward message to vTPM";
}
}
void SendCommand(cvd::SharedFD out_fd, std::vector<char> command) {
// TODO(schuffelen): Implement this logic on the host.
// TPM2 simulator command protocol.
- std::vector<char> command_bytes(4, 0);
- *reinterpret_cast<std::uint32_t*>(command_bytes.data()) = htobe32(8); // TPM_SEND_COMMAND
- CHECK(cvd::WriteAll(out_fd, command_bytes) == 4) << "Could not send TPM_SEND_COMMAND";
- CHECK(cvd::WriteAll(out_fd, std::vector<char>{(char)locality}) == 1) << "Could not send locality";
- std::vector<char> length_bytes(4, 0);
- *reinterpret_cast<std::uint32_t*>(length_bytes.data()) = htobe32(command.size());
- CHECK(cvd::WriteAll(out_fd, length_bytes) == 4) << "Could not send command length";
- CHECK(cvd::WriteAll(out_fd, command) == command.size()) << "Could not write TPM message";
+ std::uint32_t command_num = htobe32(8); // TPM_SEND_COMMAND
+ CHECK(cvd::WriteAllBinary(out_fd, &command_num) == 4)
+ << "Could not send TPM_SEND_COMMAND";
+ CHECK(cvd::WriteAllBinary(out_fd, (char*)&locality) == 1)
+ << "Could not send locality";
+ std::uint32_t length = htobe32(command.size());
+ CHECK(cvd::WriteAllBinary(out_fd, &length) == 4)
+ << "Could not send command length";
+ CHECK(cvd::WriteAll(out_fd, command) == command.size())
+ << "Could not write TPM message";
}
bool SendCommandLoop(cvd::SharedFD in_fd, cvd::SharedFD out_fd) {
@@ -80,19 +87,21 @@
// is not large enough.
// https://github.com/torvalds/linux/blob/407e9ef72476e64937ebec44cc835e03a25fb408/drivers/char/tpm/tpm_vtpm_proxy.c#L98
while ((data_length = in_fd->Read(message.data(), message.size())) < 0) {
- CHECK(in_fd->GetErrno() == EIO) << "Error in reading TPM command from kernel: "
- << in_fd->StrError();
+ CHECK(in_fd->GetErrno() == EIO) << "Error in reading TPM command from "
+ << "kernel: " << in_fd->StrError();
message.resize((message.size() + 1) * 2, '\0');
}
message.resize(data_length, 0);
auto header = reinterpret_cast<tpm_message_header*>(message.data());
- LOG(DEBUG) << "Received TPM command " << TpmCommandName(betoh32(header->ordinal));
+ LOG(DEBUG) << "Received TPM command "
+ << TpmCommandName(betoh32(header->ordinal));
if (header->ordinal == htobe32(TPM2_CC_SET_LOCALITY)) { // "Driver command"
locality = *reinterpret_cast<unsigned char*>(header + 1);
header->ordinal = htobe32(locality);
header->length = htobe32(sizeof(tpm_message_header));
message.resize(sizeof(tpm_message_header), '\0');
- CHECK(cvd::WriteAll(in_fd, message) == message.size()) << "Could not write TPM message";
+ CHECK(cvd::WriteAll(in_fd, message) == message.size())
+ << "Could not write TPM message";
} else {
SendCommand(out_fd, message);
}
diff --git a/guest/hals/keymaster/remote/Android.bp b/guest/hals/keymaster/remote/Android.bp
new file mode 100644
index 0000000..99df093
--- /dev/null
+++ b/guest/hals/keymaster/remote/Android.bp
@@ -0,0 +1,49 @@
+//
+// 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.
+
+cc_binary {
+ name: "android.hardware.keymaster@4.0-service.remote",
+ defaults: ["hidl_defaults", "cuttlefish_guest_only"],
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["android.hardware.keymaster@4.0-service.remote.rc"],
+ srcs: [
+ "service4.cpp",
+ "remote_keymaster4_device.cpp",
+ "remote_keymaster.cpp",
+ ],
+
+ static_libs: [
+ "libgflags",
+ ],
+
+ shared_libs: [
+ "android.hardware.keymaster@4.0",
+ "libbase",
+ "libcutils",
+ "libcuttlefish_fs",
+ "libcuttlefish_security",
+ "libdl",
+ "libhardware",
+ "libhidlbase",
+ "libkeymaster4",
+ "libkeymaster_messages",
+ "liblog",
+ "libtrusty",
+ "libutils",
+ ],
+
+ vintf_fragments: ["android.hardware.keymaster@4.0-service.remote.xml"],
+}
diff --git a/guest/hals/keymaster/remote/android.hardware.keymaster@4.0-service.remote.rc b/guest/hals/keymaster/remote/android.hardware.keymaster@4.0-service.remote.rc
new file mode 100644
index 0000000..422d9b6
--- /dev/null
+++ b/guest/hals/keymaster/remote/android.hardware.keymaster@4.0-service.remote.rc
@@ -0,0 +1,2 @@
+service vendor.keymaster-4-0 /vendor/bin/hw/android.hardware.keymaster@4.0-service.remote
+ class early_hal
diff --git a/guest/hals/keymaster/remote/android.hardware.keymaster@4.0-service.remote.xml b/guest/hals/keymaster/remote/android.hardware.keymaster@4.0-service.remote.xml
new file mode 100644
index 0000000..65eb854
--- /dev/null
+++ b/guest/hals/keymaster/remote/android.hardware.keymaster@4.0-service.remote.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.keymaster</name>
+ <transport>hwbinder</transport>
+ <fqname>@4.0::IKeymasterDevice/default</fqname>
+ </hal>
+</manifest>
diff --git a/guest/hals/keymaster/remote/remote_keymaster.cpp b/guest/hals/keymaster/remote/remote_keymaster.cpp
new file mode 100644
index 0000000..2b3973c
--- /dev/null
+++ b/guest/hals/keymaster/remote/remote_keymaster.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright 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 "remote_keymaster.h"
+
+#include <android-base/logging.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/keymaster_configuration.h>
+
+namespace keymaster {
+
+RemoteKeymaster::RemoteKeymaster(cvd::KeymasterChannel* channel)
+ : channel_(channel) {}
+
+RemoteKeymaster::~RemoteKeymaster() {
+}
+
+void RemoteKeymaster::ForwardCommand(AndroidKeymasterCommand command, const Serializable& req,
+ KeymasterResponse* rsp) {
+ if (!channel_->SendRequest(command, req)) {
+ LOG(ERROR) << "Failed to send keymaster message: " << command;
+ rsp->error = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ auto response = channel_->ReceiveMessage();
+ if (!response) {
+ LOG(ERROR) << "Failed to receive keymaster response: " << command;
+ rsp->error = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ const uint8_t* buffer = response->payload;
+ const uint8_t* buffer_end = response->payload + response->payload_size;
+ if (!rsp->Deserialize(&buffer, buffer_end)) {
+ LOG(ERROR) << "Failed to deserialize keymaster response: " << command;
+ rsp->error = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+}
+
+bool RemoteKeymaster::Initialize() {
+
+ ConfigureRequest req;
+ req.os_version = GetOsVersion();
+ req.os_patchlevel = GetOsPatchlevel();
+
+ ConfigureResponse rsp;
+ Configure(req, &rsp);
+
+ if (rsp.error != KM_ERROR_OK) {
+ LOG(ERROR) << "Failed to configure keymaster: " << rsp.error;
+ return false;
+ }
+
+ return true;
+}
+
+void RemoteKeymaster::GetVersion(const GetVersionRequest& request, GetVersionResponse* response) {
+ ForwardCommand(GET_VERSION, request, response);
+}
+
+void RemoteKeymaster::SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
+ SupportedAlgorithmsResponse* response) {
+ ForwardCommand(GET_SUPPORTED_ALGORITHMS, request, response);
+}
+
+void RemoteKeymaster::SupportedBlockModes(const SupportedBlockModesRequest& request,
+ SupportedBlockModesResponse* response) {
+ ForwardCommand(GET_SUPPORTED_BLOCK_MODES, request, response);
+}
+
+void RemoteKeymaster::SupportedPaddingModes(const SupportedPaddingModesRequest& request,
+ SupportedPaddingModesResponse* response) {
+ ForwardCommand(GET_SUPPORTED_PADDING_MODES, request, response);
+}
+
+void RemoteKeymaster::SupportedDigests(const SupportedDigestsRequest& request,
+ SupportedDigestsResponse* response) {
+ ForwardCommand(GET_SUPPORTED_DIGESTS, request, response);
+}
+
+void RemoteKeymaster::SupportedImportFormats(const SupportedImportFormatsRequest& request,
+ SupportedImportFormatsResponse* response) {
+ ForwardCommand(GET_SUPPORTED_IMPORT_FORMATS, request, response);
+}
+
+void RemoteKeymaster::SupportedExportFormats(const SupportedExportFormatsRequest& request,
+ SupportedExportFormatsResponse* response) {
+ ForwardCommand(GET_SUPPORTED_EXPORT_FORMATS, request, response);
+}
+
+void RemoteKeymaster::AddRngEntropy(const AddEntropyRequest& request,
+ AddEntropyResponse* response) {
+ ForwardCommand(ADD_RNG_ENTROPY, request, response);
+}
+
+void RemoteKeymaster::Configure(const ConfigureRequest& request, ConfigureResponse* response) {
+ ForwardCommand(CONFIGURE, request, response);
+}
+
+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)) {
+ datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+ }
+
+ ForwardCommand(GENERATE_KEY, datedRequest, response);
+}
+
+void RemoteKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
+ GetKeyCharacteristicsResponse* response) {
+ ForwardCommand(GET_KEY_CHARACTERISTICS, request, response);
+}
+
+void RemoteKeymaster::ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response) {
+ ForwardCommand(IMPORT_KEY, request, response);
+}
+
+void RemoteKeymaster::ImportWrappedKey(const ImportWrappedKeyRequest& request,
+ ImportWrappedKeyResponse* response) {
+ ForwardCommand(IMPORT_WRAPPED_KEY, request, response);
+}
+
+void RemoteKeymaster::ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response) {
+ ForwardCommand(EXPORT_KEY, request, response);
+}
+
+void RemoteKeymaster::AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response) {
+ ForwardCommand(ATTEST_KEY, request, response);
+}
+
+void RemoteKeymaster::UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response) {
+ ForwardCommand(UPGRADE_KEY, request, response);
+}
+
+void RemoteKeymaster::DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response) {
+ ForwardCommand(DELETE_KEY, request, response);
+}
+
+void RemoteKeymaster::DeleteAllKeys(const DeleteAllKeysRequest& request,
+ DeleteAllKeysResponse* response) {
+ ForwardCommand(DELETE_ALL_KEYS, request, response);
+}
+
+void RemoteKeymaster::BeginOperation(const BeginOperationRequest& request,
+ BeginOperationResponse* response) {
+ ForwardCommand(BEGIN_OPERATION, request, response);
+}
+
+void RemoteKeymaster::UpdateOperation(const UpdateOperationRequest& request,
+ UpdateOperationResponse* response) {
+ ForwardCommand(UPDATE_OPERATION, request, response);
+}
+
+void RemoteKeymaster::FinishOperation(const FinishOperationRequest& request,
+ FinishOperationResponse* response) {
+ ForwardCommand(FINISH_OPERATION, request, response);
+}
+
+void RemoteKeymaster::AbortOperation(const AbortOperationRequest& request,
+ AbortOperationResponse* response) {
+ ForwardCommand(ABORT_OPERATION, request, response);
+}
+
+GetHmacSharingParametersResponse RemoteKeymaster::GetHmacSharingParameters() {
+ // Dummy empty buffer to allow ForwardCommand to have something to serialize
+ Buffer request;
+ GetHmacSharingParametersResponse response;
+ ForwardCommand(GET_HMAC_SHARING_PARAMETERS, request, &response);
+ return response;
+}
+
+ComputeSharedHmacResponse RemoteKeymaster::ComputeSharedHmac(
+ const ComputeSharedHmacRequest& request) {
+ ComputeSharedHmacResponse response;
+ ForwardCommand(COMPUTE_SHARED_HMAC, request, &response);
+ return response;
+}
+
+VerifyAuthorizationResponse RemoteKeymaster::VerifyAuthorization(
+ const VerifyAuthorizationRequest& request) {
+ VerifyAuthorizationResponse response;
+ ForwardCommand(VERIFY_AUTHORIZATION, request, &response);
+ return response;
+}
+
+} // namespace keymaster
diff --git a/guest/hals/keymaster/remote/remote_keymaster.h b/guest/hals/keymaster/remote/remote_keymaster.h
new file mode 100644
index 0000000..d3f9698
--- /dev/null
+++ b/guest/hals/keymaster/remote/remote_keymaster.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef REMOTE_KEYMASTER_H_
+#define REMOTE_KEYMASTER_H_
+
+#include <keymaster/android_keymaster_messages.h>
+
+#include "common/libs/security/keymaster_channel.h"
+
+namespace keymaster {
+
+class RemoteKeymaster {
+ private:
+ cvd::KeymasterChannel* channel_;
+
+ void ForwardCommand(
+ AndroidKeymasterCommand command, const Serializable& req, KeymasterResponse* rsp);
+ public:
+ RemoteKeymaster(cvd::KeymasterChannel*);
+ ~RemoteKeymaster();
+ bool Initialize();
+ void GetVersion(const GetVersionRequest& request, GetVersionResponse* response);
+ void SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
+ SupportedAlgorithmsResponse* response);
+ void SupportedBlockModes(const SupportedBlockModesRequest& request,
+ SupportedBlockModesResponse* response);
+ void SupportedPaddingModes(const SupportedPaddingModesRequest& request,
+ SupportedPaddingModesResponse* response);
+ void SupportedDigests(const SupportedDigestsRequest& request,
+ SupportedDigestsResponse* response);
+ void SupportedImportFormats(const SupportedImportFormatsRequest& request,
+ SupportedImportFormatsResponse* response);
+ void SupportedExportFormats(const SupportedExportFormatsRequest& request,
+ SupportedExportFormatsResponse* response);
+ void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response);
+ void Configure(const ConfigureRequest& request, ConfigureResponse* response);
+ void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
+ void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
+ GetKeyCharacteristicsResponse* response);
+ void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
+ void ImportWrappedKey(const ImportWrappedKeyRequest& request,
+ ImportWrappedKeyResponse* response);
+ void ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response);
+ void AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response);
+ void UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response);
+ void DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response);
+ void DeleteAllKeys(const DeleteAllKeysRequest& request, DeleteAllKeysResponse* response);
+ void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);
+ void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response);
+ void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response);
+ void AbortOperation(const AbortOperationRequest& request, AbortOperationResponse* response);
+ GetHmacSharingParametersResponse GetHmacSharingParameters();
+ ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request);
+ VerifyAuthorizationResponse VerifyAuthorization(const VerifyAuthorizationRequest& request);
+};
+
+} // namespace keymaster
+
+#endif // REMOTE_KEYMASTER_H_
diff --git a/guest/hals/keymaster/remote/remote_keymaster4_device.cpp b/guest/hals/keymaster/remote/remote_keymaster4_device.cpp
new file mode 100644
index 0000000..ceb5231
--- /dev/null
+++ b/guest/hals/keymaster/remote/remote_keymaster4_device.cpp
@@ -0,0 +1,605 @@
+/*
+ **
+ ** Copyright 2018, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.keymaster@4.0-impl.remote"
+
+#include "remote_keymaster4_device.h"
+
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+#include <authorization_set.h>
+#include <cutils/log.h>
+#include <keymaster/android_keymaster_messages.h>
+
+using ::keymaster::AbortOperationRequest;
+using ::keymaster::AbortOperationResponse;
+using ::keymaster::AddEntropyRequest;
+using ::keymaster::AddEntropyResponse;
+using ::keymaster::AttestKeyRequest;
+using ::keymaster::AttestKeyResponse;
+using ::keymaster::AuthorizationSet;
+using ::keymaster::BeginOperationRequest;
+using ::keymaster::BeginOperationResponse;
+using ::keymaster::ExportKeyRequest;
+using ::keymaster::ExportKeyResponse;
+using ::keymaster::FinishOperationRequest;
+using ::keymaster::FinishOperationResponse;
+using ::keymaster::GenerateKeyRequest;
+using ::keymaster::GenerateKeyResponse;
+using ::keymaster::GetKeyCharacteristicsRequest;
+using ::keymaster::GetKeyCharacteristicsResponse;
+using ::keymaster::ImportKeyRequest;
+using ::keymaster::ImportKeyResponse;
+using ::keymaster::UpdateOperationRequest;
+using ::keymaster::UpdateOperationResponse;
+using ::keymaster::ng::Tag;
+
+typedef ::android::hardware::keymaster::V3_0::Tag Tag3;
+using ::android::hardware::keymaster::V4_0::Constants;
+
+namespace keymaster {
+namespace V4_0 {
+namespace {
+
+inline keymaster_tag_t legacy_enum_conversion(const Tag value) {
+ return keymaster_tag_t(value);
+}
+inline Tag legacy_enum_conversion(const keymaster_tag_t value) {
+ return Tag(value);
+}
+inline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {
+ return keymaster_purpose_t(value);
+}
+inline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {
+ return keymaster_key_format_t(value);
+}
+
+inline SecurityLevel legacy_enum_conversion(const keymaster_security_level_t value) {
+ return static_cast<SecurityLevel>(value);
+}
+
+inline hw_authenticator_type_t legacy_enum_conversion(const HardwareAuthenticatorType value) {
+ return static_cast<hw_authenticator_type_t>(value);
+}
+
+inline ErrorCode legacy_enum_conversion(const keymaster_error_t value) {
+ return ErrorCode(value);
+}
+
+inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {
+ return keymaster_tag_get_type(tag);
+}
+
+/*
+ * injectAuthToken translates a KM4 authToken into a legacy AUTH_TOKEN tag
+ *
+ * Currently, system/keymaster's reference implementation only accepts this
+ * method for passing an auth token, so until that changes we need to
+ * translate to the old format.
+ */
+inline hidl_vec<KeyParameter> injectAuthToken(const hidl_vec<KeyParameter>& keyParamsBase,
+ const HardwareAuthToken& authToken) {
+ std::vector<KeyParameter> keyParams(keyParamsBase);
+ const size_t mac_len = static_cast<size_t>(Constants::AUTH_TOKEN_MAC_LENGTH);
+ /*
+ * mac.size() == 0 indicates no token provided, so we should not copy.
+ * mac.size() != mac_len means it is incompatible with the old
+ * hw_auth_token_t structure. This is forbidden by spec, but to be safe
+ * we only copy if mac.size() == mac_len, e.g. there is an authToken
+ * with a hw_auth_token_t compatible MAC.
+ */
+ if (authToken.mac.size() == mac_len) {
+ KeyParameter p;
+ p.tag = static_cast<Tag>(Tag3::AUTH_TOKEN);
+ p.blob.resize(sizeof(hw_auth_token_t));
+
+ hw_auth_token_t* auth_token = reinterpret_cast<hw_auth_token_t*>(p.blob.data());
+ auth_token->version = 0;
+ auth_token->challenge = authToken.challenge;
+ auth_token->user_id = authToken.userId;
+ auth_token->authenticator_id = authToken.authenticatorId;
+ auth_token->authenticator_type =
+ htobe32(static_cast<uint32_t>(authToken.authenticatorType));
+ auth_token->timestamp = htobe64(authToken.timestamp);
+ static_assert(mac_len == sizeof(auth_token->hmac));
+ memcpy(auth_token->hmac, authToken.mac.data(), mac_len);
+ keyParams.push_back(p);
+ }
+
+ return hidl_vec<KeyParameter>(std::move(keyParams));
+}
+
+class KmParamSet : public keymaster_key_param_set_t {
+ public:
+ KmParamSet(const hidl_vec<KeyParameter>& keyParams) {
+ params = new keymaster_key_param_t[keyParams.size()];
+ length = keyParams.size();
+ for (size_t i = 0; i < keyParams.size(); ++i) {
+ auto tag = legacy_enum_conversion(keyParams[i].tag);
+ switch (typeFromTag(tag)) {
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);
+ break;
+ case KM_UINT:
+ case KM_UINT_REP:
+ params[i] = keymaster_param_int(tag, keyParams[i].f.integer);
+ break;
+ case KM_ULONG:
+ case KM_ULONG_REP:
+ params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);
+ break;
+ case KM_DATE:
+ params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);
+ break;
+ case KM_BOOL:
+ if (keyParams[i].f.boolValue)
+ params[i] = keymaster_param_bool(tag);
+ else
+ params[i].tag = KM_TAG_INVALID;
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ params[i] = keymaster_param_blob(tag, &keyParams[i].blob[0],
+ keyParams[i].blob.size());
+ break;
+ case KM_INVALID:
+ default:
+ params[i].tag = KM_TAG_INVALID;
+ /* just skip */
+ break;
+ }
+ }
+ }
+ KmParamSet(KmParamSet&& other) noexcept
+ : keymaster_key_param_set_t{other.params, other.length} {
+ other.length = 0;
+ other.params = nullptr;
+ }
+ KmParamSet(const KmParamSet&) = delete;
+ ~KmParamSet() { delete[] params; }
+};
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(blob.key_material), blob.key_material_size);
+ return result;
+}
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(blob.data), blob.data_length);
+ return result;
+}
+
+inline hidl_vec<uint8_t> kmBuffer2hidlVec(const ::keymaster::Buffer& buf) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(buf.peek_read()), buf.available_read());
+ return result;
+}
+
+inline static hidl_vec<hidl_vec<uint8_t>> kmCertChain2Hidl(
+ const keymaster_cert_chain_t& cert_chain) {
+ hidl_vec<hidl_vec<uint8_t>> result;
+ if (!cert_chain.entry_count || !cert_chain.entries) return result;
+
+ result.resize(cert_chain.entry_count);
+ for (size_t i = 0; i < cert_chain.entry_count; ++i) {
+ result[i] = kmBlob2hidlVec(cert_chain.entries[i]);
+ }
+
+ return result;
+}
+
+static inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {
+ hidl_vec<KeyParameter> result;
+ if (set.length == 0 || set.params == nullptr) return result;
+
+ result.resize(set.length);
+ keymaster_key_param_t* params = set.params;
+ for (size_t i = 0; i < set.length; ++i) {
+ auto tag = params[i].tag;
+ result[i].tag = legacy_enum_conversion(tag);
+ switch (typeFromTag(tag)) {
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ result[i].f.integer = params[i].enumerated;
+ break;
+ case KM_UINT:
+ case KM_UINT_REP:
+ result[i].f.integer = params[i].integer;
+ break;
+ case KM_ULONG:
+ case KM_ULONG_REP:
+ result[i].f.longInteger = params[i].long_integer;
+ break;
+ case KM_DATE:
+ result[i].f.dateTime = params[i].date_time;
+ break;
+ case KM_BOOL:
+ result[i].f.boolValue = params[i].boolean;
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ result[i].blob.setToExternal(const_cast<unsigned char*>(params[i].blob.data),
+ params[i].blob.data_length);
+ break;
+ case KM_INVALID:
+ default:
+ params[i].tag = KM_TAG_INVALID;
+ /* just skip */
+ break;
+ }
+ }
+ return result;
+}
+
+void addClientAndAppData(const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+ ::keymaster::AuthorizationSet* params) {
+ params->Clear();
+ if (clientId.size()) {
+ params->push_back(::keymaster::TAG_APPLICATION_ID, clientId.data(), clientId.size());
+ }
+ if (appData.size()) {
+ params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());
+ }
+}
+
+} // anonymous namespace
+
+RemoteKeymaster4Device::RemoteKeymaster4Device(RemoteKeymaster* impl) : impl_(impl) {}
+
+RemoteKeymaster4Device::~RemoteKeymaster4Device() {}
+
+Return<void> RemoteKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_cb) {
+ _hidl_cb(SecurityLevel::TRUSTED_ENVIRONMENT, "RemoteKeymaster", "Google");
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::getHmacSharingParameters(
+ getHmacSharingParameters_cb _hidl_cb) {
+ const GetHmacSharingParametersResponse response = impl_->GetHmacSharingParameters();
+ // response.params is not the same as the HIDL structure, we need to convert it
+ V4_0::HmacSharingParameters params;
+ params.seed.setToExternal(const_cast<uint8_t*>(response.params.seed.data),
+ response.params.seed.data_length);
+ static_assert(sizeof(response.params.nonce) == params.nonce.size(), "Nonce sizes don't match");
+ memcpy(params.nonce.data(), response.params.nonce, params.nonce.size());
+ _hidl_cb(legacy_enum_conversion(response.error), params);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::computeSharedHmac(
+ const hidl_vec<HmacSharingParameters>& params, computeSharedHmac_cb _hidl_cb) {
+ ComputeSharedHmacRequest request;
+ 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()};
+ static_assert(sizeof(request.params_array.params_array[i].nonce) ==
+ decltype(params[i].nonce)::size(),
+ "Nonce sizes don't match");
+ memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),
+ params[i].nonce.size());
+ }
+
+ auto response = impl_->ComputeSharedHmac(request);
+ hidl_vec<uint8_t> sharing_check;
+ if (response.error == KM_ERROR_OK) {
+ sharing_check = kmBlob2hidlVec(response.sharing_check);
+ }
+
+ _hidl_cb(legacy_enum_conversion(response.error), sharing_check);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::verifyAuthorization(
+ uint64_t challenge, const hidl_vec<KeyParameter>& parametersToVerify,
+ const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) {
+ VerifyAuthorizationRequest request;
+ request.challenge = challenge;
+ request.parameters_to_verify.Reinitialize(KmParamSet(parametersToVerify));
+ request.auth_token.challenge = authToken.challenge;
+ request.auth_token.user_id = authToken.userId;
+ request.auth_token.authenticator_id = authToken.authenticatorId;
+ request.auth_token.authenticator_type = legacy_enum_conversion(authToken.authenticatorType);
+ request.auth_token.timestamp = authToken.timestamp;
+ KeymasterBlob mac(authToken.mac.data(), authToken.mac.size());
+ request.auth_token.mac = mac;
+
+ auto response = impl_->VerifyAuthorization(request);
+
+ ::android::hardware::keymaster::V4_0::VerificationToken token;
+ token.challenge = response.token.challenge;
+ token.timestamp = response.token.timestamp;
+ token.parametersVerified = kmParamSet2Hidl(response.token.parameters_verified);
+ token.securityLevel = legacy_enum_conversion(response.token.security_level);
+ token.mac = kmBlob2hidlVec(response.token.mac);
+
+ _hidl_cb(legacy_enum_conversion(response.error), token);
+
+ return Void();
+}
+
+Return<ErrorCode> RemoteKeymaster4Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
+ if (data.size() == 0) return ErrorCode::OK;
+ AddEntropyRequest request;
+ request.random_data.Reinitialize(data.data(), data.size());
+
+ AddEntropyResponse response;
+ impl_->AddRngEntropy(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+
+Return<void> RemoteKeymaster4Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
+ generateKey_cb _hidl_cb) {
+ GenerateKeyRequest request;
+ request.key_description.Reinitialize(KmParamSet(keyParams));
+
+ GenerateKeyResponse response;
+ impl_->GenerateKey(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ getKeyCharacteristics_cb _hidl_cb) {
+ GetKeyCharacteristicsRequest request;
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+ addClientAndAppData(clientId, appData, &request.additional_params);
+
+ GetKeyCharacteristicsResponse response;
+ impl_->GetKeyCharacteristics(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ if (response.error == KM_ERROR_OK) {
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultCharacteristics);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::importKey(const hidl_vec<KeyParameter>& params,
+ KeyFormat keyFormat,
+ const hidl_vec<uint8_t>& keyData,
+ importKey_cb _hidl_cb) {
+ ImportKeyRequest request;
+ request.key_description.Reinitialize(KmParamSet(params));
+ request.key_format = legacy_enum_conversion(keyFormat);
+ request.SetKeyMaterial(keyData.data(), keyData.size());
+
+ ImportKeyResponse response;
+ impl_->ImportKey(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::importWrappedKey(
+ const hidl_vec<uint8_t>& wrappedKeyData, const hidl_vec<uint8_t>& wrappingKeyBlob,
+ const hidl_vec<uint8_t>& maskingKey, const hidl_vec<KeyParameter>& unwrappingParams,
+ uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) {
+ ImportWrappedKeyRequest request;
+ request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
+ request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
+ request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
+ request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
+ request.password_sid = passwordSid;
+ request.biometric_sid = biometricSid;
+
+ ImportWrappedKeyResponse response;
+ impl_->ImportWrappedKey(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::exportKey(KeyFormat exportFormat,
+ const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ exportKey_cb _hidl_cb) {
+ ExportKeyRequest request;
+ request.key_format = legacy_enum_conversion(exportFormat);
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+ addClientAndAppData(clientId, appData, &request.additional_params);
+
+ ExportKeyResponse response;
+ impl_->ExportKey(request, &response);
+
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob.setToExternal(response.key_data, response.key_data_length);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
+ const hidl_vec<KeyParameter>& attestParams,
+ attestKey_cb _hidl_cb) {
+ AttestKeyRequest request;
+ request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
+ request.attest_params.Reinitialize(KmParamSet(attestParams));
+
+ AttestKeyResponse response;
+ impl_->AttestKey(request, &response);
+
+ hidl_vec<hidl_vec<uint8_t>> resultCertChain;
+ if (response.error == KM_ERROR_OK) {
+ resultCertChain = kmCertChain2Hidl(response.certificate_chain);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultCertChain);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+ const hidl_vec<KeyParameter>& upgradeParams,
+ upgradeKey_cb _hidl_cb) {
+ UpgradeKeyRequest request;
+ request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
+ request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
+
+ UpgradeKeyResponse response;
+ impl_->UpgradeKey(request, &response);
+
+ if (response.error == KM_ERROR_OK) {
+ _hidl_cb(ErrorCode::OK, kmBlob2hidlVec(response.upgraded_key));
+ } else {
+ _hidl_cb(legacy_enum_conversion(response.error), hidl_vec<uint8_t>());
+ }
+ return Void();
+}
+
+Return<ErrorCode> RemoteKeymaster4Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
+ DeleteKeyRequest request;
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+
+ DeleteKeyResponse response;
+ impl_->DeleteKey(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> RemoteKeymaster4Device::deleteAllKeys() {
+ DeleteAllKeysRequest request;
+ DeleteAllKeysResponse response;
+ impl_->DeleteAllKeys(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> RemoteKeymaster4Device::destroyAttestationIds() {
+ return ErrorCode::UNIMPLEMENTED;
+}
+
+Return<void> RemoteKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+ const hidl_vec<KeyParameter>& inParams,
+ const HardwareAuthToken& authToken, begin_cb _hidl_cb) {
+ hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
+ BeginOperationRequest request;
+ request.purpose = legacy_enum_conversion(purpose);
+ request.SetKeyMaterial(key.data(), key.size());
+ request.additional_params.Reinitialize(KmParamSet(extendedParams));
+
+ BeginOperationResponse response;
+ impl_->BeginOperation(request, &response);
+
+ hidl_vec<KeyParameter> resultParams;
+ if (response.error == KM_ERROR_OK) {
+ resultParams = kmParamSet2Hidl(response.output_params);
+ }
+
+ _hidl_cb(legacy_enum_conversion(response.error), resultParams, response.op_handle);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::update(uint64_t operationHandle,
+ const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken,
+ update_cb _hidl_cb) {
+ (void)verificationToken;
+ UpdateOperationRequest request;
+ UpdateOperationResponse response;
+ hidl_vec<KeyParameter> resultParams;
+ hidl_vec<uint8_t> resultBlob;
+ hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
+ uint32_t resultConsumed = 0;
+
+ request.op_handle = operationHandle;
+ request.additional_params.Reinitialize(KmParamSet(extendedParams));
+
+ // TODO(schuffelen): Set a buffer size limit.
+ request.input.Reinitialize(input.data(), input.size());
+
+ impl_->UpdateOperation(request, &response);
+
+ if (response.error == KM_ERROR_OK) {
+ resultConsumed = response.input_consumed;
+ resultParams = kmParamSet2Hidl(response.output_params);
+ resultBlob = kmBuffer2hidlVec(response.output);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);
+ return Void();
+}
+
+Return<void> RemoteKeymaster4Device::finish(uint64_t operationHandle,
+ const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input,
+ const hidl_vec<uint8_t>& signature,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken,
+ finish_cb _hidl_cb) {
+ (void)verificationToken;
+ FinishOperationRequest request;
+ hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
+ request.op_handle = operationHandle;
+ request.input.Reinitialize(input.data(), input.size());
+ request.signature.Reinitialize(signature.data(), signature.size());
+ request.additional_params.Reinitialize(KmParamSet(extendedParams));
+
+ FinishOperationResponse response;
+ impl_->FinishOperation(request, &response);
+
+ hidl_vec<KeyParameter> resultParams;
+ hidl_vec<uint8_t> resultBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultParams = kmParamSet2Hidl(response.output_params);
+ resultBlob = kmBuffer2hidlVec(response.output);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultParams, resultBlob);
+ return Void();
+}
+
+Return<ErrorCode> RemoteKeymaster4Device::abort(uint64_t operationHandle) {
+ AbortOperationRequest request;
+ request.op_handle = operationHandle;
+
+ AbortOperationResponse response;
+ impl_->AbortOperation(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+} // namespace V4_0
+} // namespace keymaster
diff --git a/guest/hals/keymaster/remote/remote_keymaster4_device.h b/guest/hals/keymaster/remote/remote_keymaster4_device.h
new file mode 100644
index 0000000..5cd12b4
--- /dev/null
+++ b/guest/hals/keymaster/remote/remote_keymaster4_device.h
@@ -0,0 +1,105 @@
+/*
+ **
+ ** Copyright 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.
+ */
+
+#ifndef keymaster_V4_0_RemoteKeymaster4Device_H_
+#define keymaster_V4_0_RemoteKeymaster4Device_H_
+
+#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#include <hidl/Status.h>
+#include "guest/hals/keymaster/remote/remote_keymaster.h"
+
+namespace keymaster {
+
+namespace V4_0 {
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::keymaster::V4_0::ErrorCode;
+using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using ::android::hardware::keymaster::V4_0::HmacSharingParameters;
+using ::android::hardware::keymaster::V4_0::IKeymasterDevice;
+using ::android::hardware::keymaster::V4_0::KeyCharacteristics;
+using ::android::hardware::keymaster::V4_0::KeyFormat;
+using ::android::hardware::keymaster::V4_0::KeyParameter;
+using ::android::hardware::keymaster::V4_0::KeyPurpose;
+using ::android::hardware::keymaster::V4_0::SecurityLevel;
+using ::android::hardware::keymaster::V4_0::Tag;
+using ::android::hardware::keymaster::V4_0::VerificationToken;
+
+class RemoteKeymaster4Device : public IKeymasterDevice {
+ public:
+ explicit RemoteKeymaster4Device(RemoteKeymaster* impl);
+ virtual ~RemoteKeymaster4Device();
+
+ Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override;
+ Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override;
+ Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>& params,
+ computeSharedHmac_cb) override;
+ Return<void> verifyAuthorization(uint64_t challenge,
+ const hidl_vec<KeyParameter>& parametersToVerify,
+ const HardwareAuthToken& authToken,
+ verifyAuthorization_cb _hidl_cb) override;
+ Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
+ Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
+ generateKey_cb _hidl_cb) override;
+ Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ getKeyCharacteristics_cb _hidl_cb) override;
+ Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
+ const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;
+ Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,
+ const hidl_vec<uint8_t>& wrappingKeyBlob,
+ const hidl_vec<uint8_t>& maskingKey,
+ const hidl_vec<KeyParameter>& unwrappingParams,
+ uint64_t passwordSid, uint64_t biometricSid,
+ importWrappedKey_cb _hidl_cb) override;
+ Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+ exportKey_cb _hidl_cb) override;
+ Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
+ const hidl_vec<KeyParameter>& attestParams,
+ attestKey_cb _hidl_cb) override;
+ Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+ const hidl_vec<KeyParameter>& upgradeParams,
+ upgradeKey_cb _hidl_cb) override;
+ Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
+ Return<ErrorCode> deleteAllKeys() override;
+ Return<ErrorCode> destroyAttestationIds() override;
+ Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+ const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+ begin_cb _hidl_cb) override;
+ Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input, const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken, update_cb _hidl_cb) override;
+ Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken, finish_cb _hidl_cb) override;
+ Return<ErrorCode> abort(uint64_t operationHandle) override;
+
+ private:
+ std::unique_ptr<::keymaster::RemoteKeymaster> impl_;
+};
+
+} // namespace V4_0
+} // namespace keymaster
+
+#endif // keymaster_V4_0_RemoteKeymaster4Device_H_
diff --git a/guest/hals/keymaster/remote/service4.cpp b/guest/hals/keymaster/remote/service4.cpp
new file mode 100644
index 0000000..a5281e2
--- /dev/null
+++ b/guest/hals/keymaster/remote/service4.cpp
@@ -0,0 +1,61 @@
+/*
+**
+** Copyright 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 <android-base/logging.h>
+#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#include <cutils/properties.h>
+#include <gflags/gflags.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/security/keymaster_channel.h"
+#include <guest/hals/keymaster/remote/remote_keymaster.h>
+#include <guest/hals/keymaster/remote/remote_keymaster4_device.h>
+
+DEFINE_uint32(
+ port,
+ static_cast<uint32_t>(property_get_int64("ro.boot.vsock_keymaster_port", 0)),
+ "virtio socket port to send keymaster commands to");
+
+int main(int argc, char** argv) {
+ ::android::base::InitLogging(argv);
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ ::android::hardware::configureRpcThreadpool(1, true);
+
+ auto vsockFd = cvd::SharedFD::VsockClient(2, FLAGS_port, SOCK_STREAM);
+ if (!vsockFd->IsOpen()) {
+ LOG(FATAL) << "Could not connect to keymaster server: "
+ << vsockFd->StrError();
+ }
+ cvd::KeymasterChannel keymasterChannel(vsockFd);
+ auto remoteKeymaster = new keymaster::RemoteKeymaster(&keymasterChannel);
+
+ if (!remoteKeymaster->Initialize()) {
+ LOG(FATAL) << "Could not initialize keymaster";
+ }
+
+ auto keymaster = new ::keymaster::V4_0::RemoteKeymaster4Device(remoteKeymaster);
+
+ auto status = keymaster->registerAsService();
+ if (status != android::OK) {
+ LOG(FATAL) << "Could not register service for Keymaster 4.0 (" << status << ")";
+ return -1;
+ }
+
+ android::hardware::joinRpcThreadpool();
+ return -1; // Should never get here.
+}
diff --git a/guest/hals/ril/libril/ril_service.cpp b/guest/hals/ril/libril/ril_service.cpp
index 42e2cae..6e6b755 100644
--- a/guest/hals/ril/libril/ril_service.cpp
+++ b/guest/hals/ril/libril/ril_service.cpp
@@ -3237,12 +3237,13 @@
return Void();
}
-Return<void> RadioImpl_1_5::deactivateDataCall_1_2(int32_t /* serial */, int32_t /* cid */,
- ::android::hardware::radio::V1_2::DataRequestReason /* reason */) {
- // TODO implement
+Return<void> RadioImpl_1_5::deactivateDataCall_1_2(int32_t serial, int32_t cid,
+ ::android::hardware::radio::V1_2::DataRequestReason reason) {
#if VDBG
- RLOGE("[%04d]< %s", serial, "Method is not implemented");
+ RLOGD("deactivateDataCall_1_2: serial %d", serial);
#endif
+ deactivateDataCall(serial, cid,
+ reason == ::android::hardware::radio::V1_2::DataRequestReason::SHUTDOWN);
return Void();
}
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 34f3b1a..8e7bcc5 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
@@ -45,12 +45,9 @@
private static final int NOTIFICATION_ID = 1;
private final JobExecutor mExecutor = new JobExecutor();
- private final LocationServicesManager mLocationServices = new LocationServicesManager(this);
- private final PackageVerificationConsentEnforcer mConsentEnforcer = new PackageVerificationConsentEnforcer(this);
private final BootReporter mBootReporter = new BootReporter();
private final GceBroadcastReceiver mBroadcastReceiver = new GceBroadcastReceiver();
private final BluetoothChecker mBluetoothChecker = new BluetoothChecker();
- private final TombstoneChecker mTombstoneChecker = new TombstoneChecker();
private ConnectivityChecker mConnChecker;
private GceWifiManager mWifiManager = null;
@@ -69,20 +66,11 @@
mConnChecker = new ConnectivityChecker(this, mBootReporter);
mWifiManager = new GceWifiManager(this, mBootReporter, mExecutor);
- mExecutor.schedule(mLocationServices);
- mExecutor.schedule(mConsentEnforcer);
mExecutor.schedule(mWifiManager);
mExecutor.schedule(mBluetoothChecker);
mExecutor.schedule(mConnChecker);
- // TODO(ender): TombstoneChecker is disabled, because we no longer have the code that
- // produces /ts_snap.txt file. We need to rethink how TombstoneChecker should work.
- // mExecutor.schedule(mTombstoneChecker);
- mExecutor.schedule(mBootReporter,
- mLocationServices.getLocationServicesReady(),
- mBluetoothChecker.getEnabled()
- // mTombstoneChecker.getTombstoneResult()
- );
+ mExecutor.schedule(mBootReporter, mBluetoothChecker.getEnabled());
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
@@ -171,13 +159,9 @@
}
pw.println("");
pw.println("Current system service state:");
- pw.println(" Location service ready: "
- + mLocationServices.getLocationServicesReady().isDone());
pw.println(" Network connected: " + mConnChecker.getConnected().isDone());
pw.println(" WiFi configured: " + mWifiManager.getWifiReady().isDone());
pw.println(" Bluetooth enabled: " + mBluetoothChecker.getEnabled().isDone());
- pw.println(" Tombstone dropped (on boot): "
- + !mTombstoneChecker.getTombstoneResult().isDone());
pw.println("");
}
}
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceWifiManager.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceWifiManager.java
index 2747110..1b07c04 100644
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceWifiManager.java
+++ b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceWifiManager.java
@@ -77,12 +77,6 @@
return mMonitorWifiJob.getWifiReady();
}
-
- /* Modifies Wifi state:
- * - if wifi disable requested (state == false), simply turns off wifi.
- * - if wifi enable requested (state == true), turns on wifi and arms the
- * connection timeout (see startWifiReconnectionTimeout).
- */
private class MonitorWifiJob extends JobBase {
private final GceFuture<Boolean> mWifiReady =
new GceFuture<Boolean>("WIFI Ready");
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/LocationServicesManager.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/LocationServicesManager.java
deleted file mode 100644
index f9ad388..0000000
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/LocationServicesManager.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.google.gce.gceservice;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.util.Log;
-import com.android.google.gce.gceservice.GceFuture;
-import com.android.google.gce.gceservice.JobBase;
-
-/**
- * Configure Location Services on Android Jellybean.
- * No action on more recent versions of Android.
- */
-class LocationServicesManager extends JobBase {
- private static final String LOG_TAG = "GceLocationServicesManager";
- private static final String ACTION_LOCATION_SERVICES_CONSENT_INTENT =
- "com.google.android.gsf.action.SET_USE_LOCATION_FOR_SERVICES";
- private static final String EXTRA_LOCATION_SERVICES_CONSENT_DISABLE =
- "disable";
- private final Context mContext;
- private final GceFuture<Boolean> mResult = new GceFuture<Boolean>("Location Services");
-
-
- LocationServicesManager(Context context) {
- super(LOG_TAG);
- mContext = context;
- }
-
-
- public int execute() {
- /* Check if we're running Jellybean.
- * Sadly, we can't use version name Build.VERSION_CODES.JELLY_BEAN_MR2
- * because MR1 and MR0 don't know this number.
- */
- if (Build.VERSION.SDK_INT <= 18) {
- Intent intent = new Intent();
- intent.setAction(ACTION_LOCATION_SERVICES_CONSENT_INTENT);
- intent.setFlags(intent.getFlags() |
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(EXTRA_LOCATION_SERVICES_CONSENT_DISABLE, false);
- mContext.startActivity(intent);
- }
-
- mResult.set(true);
- return 0;
- }
-
-
- public void onDependencyFailed(Exception e) {
- Log.e(LOG_TAG, "Could not configure LocationServices.", e);
- mResult.set(e);
- }
-
-
- public GceFuture<Boolean> getLocationServicesReady() {
- return mResult;
- }
-}
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/PackageVerificationConsentEnforcer.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/PackageVerificationConsentEnforcer.java
deleted file mode 100644
index e45d656..0000000
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/PackageVerificationConsentEnforcer.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.google.gce.gceservice;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.util.Log;
-
-/**
- * Forces pacakge verification to be off on N and N-MR1 by adjusting package_verifier_user_consent.
- *
- * This is needed because CVDs don't have a touch screen, and the consent
- * dialog will block apk installs.
- *
- * Possible values for consent seem to be:
- * -1 The user refused
- * 0 Ask the user
- * 1 The user accepted
- *
- * This code polls because Android may overwrite a non-zero value with a 0
- * at some point after boot completes. However, this happens only on some
- * boots, so it can't be a blocker for boot complete.
- */
-class PackageVerificationConsentEnforcer extends JobBase {
- private static final String LOG_TAG = "GcePVCR";
- private static final String PACKAGE_VERIFIER_USER_CONSENT = "package_verifier_user_consent";
- private final Context mContext;
-
- // Chosen to avoid the possible values (see top comment).
- private int mLastObservedValue = -2;
-
-
- public PackageVerificationConsentEnforcer(Context context) {
- super(LOG_TAG);
- mContext = context;
- }
-
-
- public int execute() {
- if (android.os.Build.VERSION.SDK_INT < 24) {
- // Skip older android versions.
- return 0;
- }
-
- try {
- ContentResolver contentResolver = mContext.getContentResolver();
- int value = Settings.Secure.getInt(contentResolver, PACKAGE_VERIFIER_USER_CONSENT);
- if (value != mLastObservedValue) {
- mLastObservedValue = value;
- }
-
- if (value == 0) {
- Settings.Secure.putInt(mContext.getContentResolver(), PACKAGE_VERIFIER_USER_CONSENT, -1);
- }
- } catch (SettingNotFoundException e) {
- }
-
- return 1;
- }
-
-
- public void onDependencyFailed(Exception e) {
- Log.e(LOG_TAG, "Could not start Consent Enforcer.", e);
- }
-}
-
-
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/TombstoneChecker.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/TombstoneChecker.java
deleted file mode 100644
index dec0837..0000000
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/TombstoneChecker.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.google.gce.gceservice;
-
-import android.util.Log;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Scanner;
-
-/** A job that checks for any new tombstones before reporting VIRTUAL_DEVICE_BOOT_COMPLETED.
- *
- */
-public class TombstoneChecker extends JobBase {
- private static final String LOG_TAG = "GceTombstoneChecker";
- private static final String sSnapshotDir = "/data/tombstones";
- private static final String sSnapshotFile = "/ts_snap.txt";
- private static final String sTsExceptionMessage = "GceTombstoneChecker internal error. ";
- private static final String sTsFilePrefix = "tombstone";
- private final GceFuture<Boolean> mPassed = new GceFuture<Boolean>("GceTombstoneChecker");
- private ArrayList<Record> mPreBootRecords = new ArrayList<Record>();
- private ArrayList<Record> mPostBootRecords = new ArrayList<Record>();
-
- public TombstoneChecker() {
- super(LOG_TAG);
- }
-
-
- @Override
- public int execute() {
- if (mPassed.isDone()) {
- return 0;
- }
-
- try {
- readPreBootSnapshot();
- capturePostBootSnapshot();
- if (seenNewTombstones()) {
- Log.e(LOG_TAG, "Tombstones created during boot. ");
- for (int i = 0; i < mPostBootRecords.size(); i++) {
- Log.i(LOG_TAG, mPostBootRecords.get(i).getFileName());
- }
- mPassed.set(new Exception("Tombstones created. "));
- } else {
- mPassed.set(true);
- }
- } catch(Exception e) {
- Log.e(LOG_TAG, sTsExceptionMessage + e);
- mPassed.set(new Exception(sTsExceptionMessage, e));
- }
-
- return 0;
- }
-
- @Override
- public void onDependencyFailed(Exception e) {
- mPassed.set(e);
- }
-
- public GceFuture<Boolean> getTombstoneResult() {
- return mPassed;
- }
-
- private void capturePostBootSnapshot() throws Exception {
- File dir = new File(sSnapshotDir);
- File[] files = dir.listFiles();
-
- // In K & L, /data/tombstones directory is not created during boot. So
- // dir.listFiles() can return null.
- if (files == null) {
- return;
- }
-
- for (int i = 0; i < files.length; i++) {
- if (files[i].isFile() && files[i].getName().startsWith(sTsFilePrefix)) {
- long ctime = files[i].lastModified() / 1000;
- mPostBootRecords.add(new Record(files[i].getName(), ctime));
- }
- }
- Collections.sort(mPostBootRecords);
-
- return;
- }
-
- private void readPreBootSnapshot() throws Exception {
- File file = new File(sSnapshotFile);
- if (!file.isFile()) {
- throw new FileNotFoundException(sSnapshotFile);
- }
-
- Scanner scanner = new Scanner(file);
- while (scanner.hasNext()) {
- String[] fields = scanner.nextLine().split(" ");
- mPreBootRecords.add(new Record(fields[0], Long.parseLong(fields[1])));
- }
- Collections.sort(mPreBootRecords);
-
- return;
- }
-
- private boolean seenNewTombstones() {
- return !isEqual(mPreBootRecords, mPostBootRecords);
- }
-
- private boolean isEqual(ArrayList<Record> preBoot, ArrayList<Record> postBoot) {
- postBoot.removeAll(preBoot);
- if (postBoot.size() != 0) {
- return false;
- }
-
- return true;
- }
-
- private class Record implements Comparable<Record> {
- private String mFilename;
- private long mCtime;
-
- public Record(String filename, long ctime) {
- this.mFilename = filename;
- this.mCtime = ctime;
- }
-
- public String getFileName() {
- return mFilename;
- }
-
- public int compareTo(Record r) {
- if (this == r) {
- return 0;
- }
-
- return (mFilename.compareTo(r.mFilename));
- }
-
- public boolean equals(Object o) {
- if (o == null) {
- return false;
- }
-
- if (this == o) {
- return true;
- }
-
- Record r = (Record) o;
- return (mFilename.equals(r.mFilename) && (mCtime == r.mCtime));
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(mFilename).append(" ").append(String.valueOf(mCtime));
- return sb.toString();
- }
- }
-}
diff --git a/host/commands/assemble_cvd/Android.bp b/host/commands/assemble_cvd/Android.bp
index 08bfb78..d19d3e4 100644
--- a/host/commands/assemble_cvd/Android.bp
+++ b/host/commands/assemble_cvd/Android.bp
@@ -32,6 +32,7 @@
name: "assemble_cvd",
srcs: [
"assemble_cvd.cc",
+ "boot_config.cc",
"boot_image_unpacker.cc",
"data_image.cc",
"flags.cc",
diff --git a/host/commands/assemble_cvd/boot_config.cc b/host/commands/assemble_cvd/boot_config.cc
new file mode 100644
index 0000000..181a111
--- /dev/null
+++ b/host/commands/assemble_cvd/boot_config.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/commands/assemble_cvd/boot_config.h"
+
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <sys/stat.h>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "common/libs/utils/files.h"
+#include "common/libs/utils/subprocess.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/kernel_args.h"
+
+namespace {
+
+size_t WriteEnvironment(const vsoc::CuttlefishConfig& config,
+ const std::string& env_path) {
+ std::ostringstream env;
+ auto kernel_args = KernelCommandLineFromConfig(config);
+ env << "bootargs=" << android::base::Join(kernel_args, " ") << '\0';
+ if (!config.boot_slot().empty()) {
+ env << "android_slot_suffix=_" << config.boot_slot() << '\0';
+ }
+ env << "bootdevice=0:1" << '\0';
+ env << "bootdelay=0" << '\0';
+ env << "bootcmd=boot_android virtio -" << '\0';
+ env << '\0';
+ std::string env_str = env.str();
+ std::ofstream file_out(env_path.c_str(), std::ios::binary);
+ file_out << env_str;
+
+ if(!file_out.good()) {
+ return 0;
+ }
+
+ return env_str.length();
+}
+
+} // namespace
+
+
+bool InitBootloaderEnvPartition(const vsoc::CuttlefishConfig& config,
+ const std::string& boot_env_image_path) {
+ auto tmp_boot_env_image_path = boot_env_image_path + ".tmp";
+ auto uboot_env_path = config.AssemblyPath("u-boot.env");
+ if(!WriteEnvironment(config, uboot_env_path)) {
+ LOG(ERROR) << "Unable to write out plaintext env '" << uboot_env_path << ".'";
+ return false;
+ }
+
+ auto mkimage_path = vsoc::DefaultHostArtifactsPath("bin/mkenvimage");
+ cvd::Command cmd(mkimage_path);
+ cmd.AddParameter("-s");
+ cmd.AddParameter("4096");
+ cmd.AddParameter("-o");
+ cmd.AddParameter(tmp_boot_env_image_path);
+ cmd.AddParameter(uboot_env_path);
+ int success = cmd.Start().Wait();
+ if (success != 0) {
+ LOG(ERROR) << "Unable to run mkenvimage. Exited with status " << success;
+ return false;
+ }
+
+ if(!cvd::FileExists(boot_env_image_path) || cvd::ReadFile(boot_env_image_path) != cvd::ReadFile(tmp_boot_env_image_path)) {
+ if(!cvd::RenameFile(tmp_boot_env_image_path, boot_env_image_path)) {
+ LOG(ERROR) << "Unable to delete the old env image.";
+ return false;
+ }
+ LOG(INFO) << "Updated bootloader environment image.";
+ } else {
+ cvd::RemoveFile(tmp_boot_env_image_path);
+ }
+
+ return true;
+}
diff --git a/host/commands/assemble_cvd/boot_config.h b/host/commands/assemble_cvd/boot_config.h
new file mode 100644
index 0000000..a7b6a12
--- /dev/null
+++ b/host/commands/assemble_cvd/boot_config.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <string>
+#include <host/libs/config/cuttlefish_config.h>
+
+bool InitBootloaderEnvPartition(const vsoc::CuttlefishConfig& config,
+ const std::string& boot_env_image_path);
diff --git a/host/commands/assemble_cvd/data_image.cc b/host/commands/assemble_cvd/data_image.cc
index b1c371d..bbda55d 100644
--- a/host/commands/assemble_cvd/data_image.cc
+++ b/host/commands/assemble_cvd/data_image.cc
@@ -2,9 +2,13 @@
#include <android-base/logging.h>
+#include "common/libs/fs/shared_buf.h"
+
#include "common/libs/utils/files.h"
#include "common/libs/utils/subprocess.h"
+#include "host/commands/assemble_cvd/mbr.h"
+
namespace {
const std::string kDataPolicyUseExisting = "use_existing";
const std::string kDataPolicyCreateIfMissing = "create_if_missing";
@@ -36,11 +40,10 @@
return true;
} else {
off_t raw_target = static_cast<off_t>(data_image_mb) << 20;
- int truncate_status =
- cvd::SharedFD::Open(data_image, O_RDWR)->Truncate(raw_target);
- if (truncate_status != 0) {
+ auto fd = cvd::SharedFD::Open(data_image, O_RDWR);
+ if (fd->Truncate(raw_target) != 0) {
LOG(ERROR) << "`truncate --size=" << data_image_mb << "M "
- << data_image << "` failed with code " << truncate_status;
+ << data_image << "` failed:" << fd->StrError();
return false;
}
bool fsck_success = ForceFsckImage(data_image);
@@ -64,20 +67,58 @@
} // namespace
void CreateBlankImage(
- const std::string& image, int block_count, const std::string& image_fmt,
- const std::string& block_size) {
+ const std::string& image, int num_mb, const std::string& image_fmt) {
LOG(INFO) << "Creating " << image;
- std::string of = "of=";
- of += image;
- std::string count = "count=";
- count += std::to_string(block_count);
- std::string bs = "bs=" + block_size;
- cvd::execute({"/bin/dd", "if=/dev/zero", of, bs, count});
+
+ off_t image_size_bytes = static_cast<off_t>(num_mb) << 20;
+ // The newfs_msdos tool with the mandatory -C option will do the same
+ // as below to zero the image file, so we don't need to do it here
+ if (image_fmt != "sdcard") {
+ auto fd = cvd::SharedFD::Open(image, O_CREAT | O_TRUNC | O_RDWR, 0666);
+ if (fd->Truncate(image_size_bytes) != 0) {
+ LOG(ERROR) << "`truncate --size=" << num_mb << "M " << image
+ << "` failed:" << fd->StrError();
+ return;
+ }
+ }
+
if (image_fmt == "ext4") {
cvd::execute({"/sbin/mkfs.ext4", image});
- } else if (image_fmt != "none") {
+ } else if (image_fmt == "f2fs") {
auto make_f2fs_path = vsoc::DefaultHostArtifactsPath("bin/make_f2fs");
cvd::execute({make_f2fs_path, "-t", image_fmt, image, "-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;
+ off_t image_size_sectors = image_size_bytes / 512;
+ auto newfs_msdos_path = vsoc::DefaultHostArtifactsPath("bin/newfs_msdos");
+ cvd::execute({newfs_msdos_path, "-F", "32", "-m", "0xf8", "-a", "4088",
+ "-o", "0", "-c", "8", "-h", "255",
+ "-u", "63", "-S", "512",
+ "-s", std::to_string(image_size_sectors),
+ "-C", std::to_string(num_mb) + "M",
+ "-@", std::to_string(offset_size_bytes),
+ image});
+ // Write the MBR after the filesystem is formatted, as the formatting tools
+ // don't consistently preserve the image contents
+ MasterBootRecord mbr = {
+ .partitions = {{
+ .partition_type = 0xC,
+ .first_lba = (std::uint32_t) offset_size_bytes / SECTOR_SIZE,
+ .num_sectors = (std::uint32_t) image_size_bytes / SECTOR_SIZE,
+ }},
+ .boot_signature = { 0x55, 0xAA },
+ };
+ auto fd = cvd::SharedFD::Open(image, O_RDWR);
+ if (cvd::WriteAllBinary(fd, &mbr) != sizeof(MasterBootRecord)) {
+ LOG(ERROR) << "Writing MBR to " << image << " failed:" << fd->StrError();
+ return;
+ }
+ } else if (image_fmt != "none") {
+ LOG(WARNING) << "Unknown image format '" << image_fmt
+ << "' for " << image << ", treating as 'none'.";
}
}
diff --git a/host/commands/assemble_cvd/data_image.h b/host/commands/assemble_cvd/data_image.h
index e83b4ee..e4afdf8 100644
--- a/host/commands/assemble_cvd/data_image.h
+++ b/host/commands/assemble_cvd/data_image.h
@@ -14,5 +14,4 @@
const std::string& path);
bool InitializeMiscImage(const std::string& misc_image);
void CreateBlankImage(
- const std::string& image, int block_count, const std::string& image_fmt,
- const std::string& block_size = "1M");
+ const std::string& image, int num_mb, const std::string& image_fmt);
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 6df46ef..76e97c3 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -17,6 +17,7 @@
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
+#include "host/commands/assemble_cvd/boot_config.h"
#include "host/commands/assemble_cvd/boot_image_unpacker.h"
#include "host/commands/assemble_cvd/data_image.h"
#include "host/commands/assemble_cvd/image_aggregator.h"
@@ -27,6 +28,9 @@
#include "host/libs/vm_manager/qemu_manager.h"
#include "host/libs/vm_manager/vm_manager.h"
+// Taken from external/avb/libavb/avb_slot_verify.c; this define is not in the headers
+#define VBMETA_MAX_SIZE 65536ul
+
using vsoc::ForCurrentInstance;
using cvd::AssemblerExitCodes;
@@ -35,6 +39,8 @@
"to be generated.");
DEFINE_int32(blank_metadata_image_mb, 16,
"The size of the blank metadata image to generate, MB.");
+DEFINE_int32(blank_sdcard_image_mb, 2048,
+ "The size of the blank sdcard image to generate, MB.");
DEFINE_int32(cpus, 2, "Virtual CPU count.");
DEFINE_string(data_image, "", "Location of the data partition image.");
DEFINE_string(data_policy, "use_existing", "How to handle userdata partition."
@@ -71,6 +77,12 @@
DEFINE_string(vendor_boot_image, "",
"Location of cuttlefish vendor boot image. If empty it is assumed to "
"be vendor_boot.img in the directory specified by -system_image_dir.");
+DEFINE_string(vbmeta_image, "",
+ "Location of cuttlefish vbmeta image. If empty it is assumed to "
+ "be vbmeta.img in the directory specified by -system_image_dir.");
+DEFINE_string(vbmeta_system_image, "",
+ "Location of cuttlefish vbmeta_system image. If empty it is assumed to "
+ "be vbmeta_system.img in the directory specified by -system_image_dir.");
DEFINE_int32(memory_mb, 2048,
"Total amount of memory available for guest, MB.");
DEFINE_string(serial_number, ForCurrentInstance("CUTTLEFISHCVD"),
@@ -94,6 +106,9 @@
DEFINE_string(misc_image, "",
"Location of the misc partition image. If the image does not "
"exist, a blank new misc partition image is created.");
+DEFINE_string(boot_env_image, "",
+ "Location of the boot environment image. If the image does not "
+ "exist, a default boot environment image is created.");
DEFINE_bool(deprecated_boot_completed, false, "Log boot completed message to"
" host kernel. This is only used during transition of our clients."
@@ -129,7 +144,7 @@
vsoc::DefaultHostArtifactsPath(kSeccompDir),
"With sandbox'ed crosvm, overrieds the security comp policy directory");
-DEFINE_bool(start_webrtc, false, "[Experimental] Whether to start the webrtc process.");
+DEFINE_bool(start_webrtc, false, "Whether to start the webrtc process.");
DEFINE_string(
webrtc_assets_dir,
@@ -151,6 +166,35 @@
false,
"[Experimental] If enabled, exposes local adb service through a websocket.");
+DEFINE_bool(
+ start_webrtc_sig_server, false,
+ "Whether to start the webrtc signaling server. This option only applies to "
+ "the first instance, if multiple instances are launched they'll share the "
+ "same signaling server, which is owned by the first one.");
+
+DEFINE_string(webrtc_sig_server_addr, "127.0.0.1",
+ "The address of the webrtc signaling server.");
+
+DEFINE_int32(
+ webrtc_sig_server_port, 443,
+ "The port of the signaling server if started outside of this launch. If "
+ "-start_webrtc_sig_server is given it will choose 8443+instance_num1-1 and "
+ "this parameter is ignored.");
+
+DEFINE_string(webrtc_sig_server_path, "/register_device",
+ "The path section of the URL where the device should be "
+ "registered with the signaling server.");
+
+DEFINE_bool(verify_sig_server_certificate, false,
+ "Whether to verify the signaling server's certificate with a "
+ "trusted signing authority (Disallow self signed certificates).");
+
+DEFINE_string(
+ webrtc_device_id, "cvd-{num}",
+ "The for the device to register with the signaling server. Every "
+ "appearance of the substring '{num}' in the device id will be substituted "
+ "with the instance number to support multiple instances");
+
DEFINE_string(adb_mode, "vsock_half_tunnel",
"Mode for ADB connection."
"'vsock_tunnel' for a TCP connection tunneled through vsock, "
@@ -179,8 +223,7 @@
DEFINE_string(crosvm_binary,
vsoc::DefaultHostArtifactsPath("bin/crosvm"),
"The Crosvm binary to use");
-DEFINE_string(tpm_binary,
- vsoc::DefaultHostArtifactsPath("bin/ms-tpm-20-ref"),
+DEFINE_string(tpm_binary, "",
"The TPM simulator to use. Disabled if empty.");
DEFINE_string(tpm_device, "", "A host TPM device to pass through commands to.");
DEFINE_bool(restart_subprocesses, true, "Restart any crashed host process");
@@ -202,6 +245,7 @@
"images have been updated since the first launch.");
DEFINE_string(report_anonymous_usage_stats, "", "Report anonymous usage "
"statistics for metrics collection and analysis.");
+DEFINE_string(ril_dns, "8.8.8.8", "DNS address of mobile network (RIL)");
namespace {
@@ -240,6 +284,17 @@
SetCommandLineOptionWithMode("vendor_boot_image",
default_vendor_boot_image.c_str(),
google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_boot_env_image = FLAGS_system_image_dir + "/env.img";
+ SetCommandLineOptionWithMode("boot_env_image", default_boot_env_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_vbmeta_image = FLAGS_system_image_dir + "/vbmeta.img";
+ SetCommandLineOptionWithMode("vbmeta_image", default_vbmeta_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_vbmeta_system_image = FLAGS_system_image_dir
+ + "/vbmeta_system.img";
+ SetCommandLineOptionWithMode("vbmeta_system_image",
+ default_vbmeta_system_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
return true;
}
@@ -308,8 +363,9 @@
tmp_config_obj.AssemblyPath(kKernelDefaultPath.c_str()));
tmp_config_obj.set_use_unpacked_kernel(true);
}
+
tmp_config_obj.set_decompress_kernel(FLAGS_decompress_kernel);
- if (FLAGS_decompress_kernel) {
+ if (tmp_config_obj.decompress_kernel()) {
tmp_config_obj.set_decompressed_kernel_image_path(
tmp_config_obj.AssemblyPath("vmlinux"));
}
@@ -375,6 +431,13 @@
tmp_config_obj.set_webrtc_assets_dir(FLAGS_webrtc_assets_dir);
tmp_config_obj.set_webrtc_public_ip(FLAGS_webrtc_public_ip);
tmp_config_obj.set_webrtc_certs_dir(FLAGS_webrtc_certs_dir);
+ tmp_config_obj.set_sig_server_binary(
+ vsoc::DefaultHostArtifactsPath("bin/webrtc_sig_server"));
+ // 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);
+ tmp_config_obj.set_sig_server_path(FLAGS_webrtc_sig_server_path);
+ tmp_config_obj.set_sig_server_strict(FLAGS_verify_sig_server_certificate);
tmp_config_obj.set_webrtc_enable_adb_websocket(
FLAGS_webrtc_enable_adb_websocket);
@@ -410,11 +473,14 @@
tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
+ tmp_config_obj.set_ril_dns(FLAGS_ril_dns);
+
std::vector<int> instance_nums;
for (int i = 0; i < FLAGS_num_instances; i++) {
instance_nums.push_back(vsoc::GetInstance() + i);
}
+ bool is_first_instance = true;
for (const auto& num : instance_nums) {
auto instance = tmp_config_obj.ForInstance(num);
auto const_instance = const_cast<const vsoc::CuttlefishConfig&>(tmp_config_obj)
@@ -449,10 +515,37 @@
instance.set_keyboard_server_port(7000 + num - 1);
instance.set_touch_server_port(7100 + num - 1);
}
+ instance.set_keymaster_vsock_port(7200 + num - 1);
instance.set_device_title(FLAGS_device_title);
- instance.set_virtual_disk_paths({const_instance.PerInstancePath("overlay.img")});
+ instance.set_virtual_disk_paths({
+ const_instance.PerInstancePath("overlay.img"),
+ const_instance.sdcard_path(),
+ });
+
+ instance.set_start_webrtc_signaling_server(false);
+
+ if (FLAGS_webrtc_device_id.empty()) {
+ // Use the instance's name as a default
+ instance.set_webrtc_device_id(const_instance.instance_name());
+ } else {
+ std::string device_id = FLAGS_webrtc_device_id;
+ size_t pos;
+ while ((pos = device_id.find("{num}")) != std::string::npos) {
+ device_id.replace(pos, strlen("{num}"), std::to_string(num));
+ }
+ instance.set_webrtc_device_id(device_id);
+ }
+ if (FLAGS_start_webrtc_sig_server && is_first_instance) {
+ auto port = 8443 + num - 1;
+ // Change the signaling server port for all instances
+ tmp_config_obj.set_sig_server_port(port);
+ instance.set_start_webrtc_signaling_server(true);
+ } else {
+ instance.set_start_webrtc_signaling_server(false);
+ }
+ is_first_instance = false;
}
return tmp_config_obj;
@@ -492,7 +585,7 @@
google::FlagSettingMode::SET_FLAGS_DEFAULT);
// for now, we support only x86_64 by default
bool default_enable_sandbox = false;
- std::set<const std::string> supported_archs{std::string("x86_64"), std::string("aarch64")};
+ std::set<const std::string> supported_archs{std::string("x86_64")};
if (supported_archs.find(cvd::HostArch()) != supported_archs.end()) {
default_enable_sandbox =
[](const std::string& var_empty) -> bool {
@@ -515,6 +608,16 @@
SetCommandLineOptionWithMode("enable_sandbox",
(default_enable_sandbox ? "true" : "false"),
google::FlagSettingMode::SET_FLAGS_DEFAULT);
+
+ // Crosvm requires a specific setting for kernel decompression; it must be
+ // on for aarch64 and off for x86, no other mode is supported.
+ bool decompress_kernel = false;
+ if (cvd::HostArch() == "aarch64") {
+ decompress_kernel = true;
+ }
+ SetCommandLineOptionWithMode("decompress_kernel",
+ (decompress_kernel ? "true" : "false"),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
}
bool ParseCommandLineFlags(int* argc, char*** argv) {
@@ -536,6 +639,17 @@
SetCommandLineOptionWithMode("start_vnc_server", "true",
google::FlagSettingMode::SET_FLAGS_DEFAULT);
}
+ // Various temporary workarounds for aarch64
+ if (cvd::HostArch() == "aarch64") {
+ SetCommandLineOptionWithMode("tpm_binary",
+ "",
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ }
+ // The default for starting signaling server is whether or not webrt is to be
+ // started.
+ SetCommandLineOptionWithMode("start_webrtc_sig_server",
+ FLAGS_start_webrtc ? "true" : "false",
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
google::HandleCommandLineHelpFlags();
if (invalid_manager) {
return false;
@@ -678,6 +792,50 @@
std::vector<ImagePartition> disk_config() {
std::vector<ImagePartition> partitions;
+
+ // Note that if the positions of env or misc change, the environment for
+ // u-boot must be updated as well (see boot_config.cc and
+ // configs/cf-x86_defconfig in external/u-boot).
+ partitions.push_back(ImagePartition {
+ .label = "env",
+ .image_file_path = FLAGS_boot_env_image,
+ });
+ partitions.push_back(ImagePartition {
+ .label = "misc",
+ .image_file_path = FLAGS_misc_image,
+ });
+ partitions.push_back(ImagePartition {
+ .label = "boot_a",
+ .image_file_path = FLAGS_boot_image,
+ });
+ partitions.push_back(ImagePartition {
+ .label = "boot_b",
+ .image_file_path = FLAGS_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,
+ });
+ partitions.push_back(ImagePartition {
+ .label = "vbmeta_b",
+ .image_file_path = FLAGS_vbmeta_image,
+ });
+ partitions.push_back(ImagePartition {
+ .label = "vbmeta_system_a",
+ .image_file_path = FLAGS_vbmeta_system_image,
+ });
+ partitions.push_back(ImagePartition {
+ .label = "vbmeta_system_b",
+ .image_file_path = FLAGS_vbmeta_system_image,
+ });
partitions.push_back(ImagePartition {
.label = "super",
.image_file_path = FLAGS_super_image,
@@ -694,14 +852,6 @@
.label = "metadata",
.image_file_path = FLAGS_metadata_image,
});
- partitions.push_back(ImagePartition {
- .label = "boot",
- .image_file_path = FLAGS_boot_image,
- });
- partitions.push_back(ImagePartition {
- .label = "misc",
- .image_file_path = FLAGS_misc_image
- });
return partitions;
}
@@ -816,6 +966,7 @@
preserving.insert("gpt_header.img");
preserving.insert("gpt_footer.img");
preserving.insert("composite.img");
+ preserving.insert("sdcard.img");
preserving.insert("access-kregistry");
preserving.insert("disk_hole");
preserving.insert("NVChip");
@@ -952,13 +1103,36 @@
exit(cvd::kCuttlefishConfigurationInitError);
}
+ // Create boot_config if necessary
+ if (!InitBootloaderEnvPartition(*config, FLAGS_boot_env_image)) {
+ exit(cvd::kCuttlefishConfigurationInitError);
+ }
+
if (!cvd::FileExists(FLAGS_metadata_image)) {
CreateBlankImage(FLAGS_metadata_image, FLAGS_blank_metadata_image_mb, "none");
}
for (const auto& instance : config->Instances()) {
if (!cvd::FileExists(instance.access_kregistry_path())) {
- CreateBlankImage(instance.access_kregistry_path(), 2, "none", "1M");
+ CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none");
+ }
+
+ if (!cvd::FileExists(instance.sdcard_path())) {
+ CreateBlankImage(instance.sdcard_path(),
+ FLAGS_blank_sdcard_image_mb, "sdcard");
+ }
+ }
+
+ // libavb expects to be able to read the maximum vbmeta size, so we must
+ // provide a partition which matches this or the read will fail
+ for (const auto& vbmeta_image : { FLAGS_vbmeta_image, FLAGS_vbmeta_system_image }) {
+ if (cvd::FileSize(vbmeta_image) != VBMETA_MAX_SIZE) {
+ auto fd = cvd::SharedFD::Open(vbmeta_image, O_RDWR);
+ if (fd->Truncate(VBMETA_MAX_SIZE) != 0) {
+ LOG(ERROR) << "`truncate --size=" << VBMETA_MAX_SIZE << " "
+ << vbmeta_image << "` failed: " << fd->StrError();
+ exit(cvd::kCuttlefishConfigurationInitError);
+ }
}
}
@@ -988,7 +1162,7 @@
<< "newer than its underlying composite disk. Wiping the overlay.";
}
CreateQcowOverlay(config->crosvm_binary(), config->composite_disk_path(), overlay_path);
- CreateBlankImage(instance.access_kregistry_path(), 2, "none", "1M");
+ CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none");
}
}
diff --git a/host/commands/assemble_cvd/image_aggregator.cc b/host/commands/assemble_cvd/image_aggregator.cc
index 04ec6b3..7be4aaa 100644
--- a/host/commands/assemble_cvd/image_aggregator.cc
+++ b/host/commands/assemble_cvd/image_aggregator.cc
@@ -40,31 +40,14 @@
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/files.h"
#include "common/libs/utils/subprocess.h"
+#include "host/commands/assemble_cvd/mbr.h"
#include "host/libs/config/cuttlefish_config.h"
#include "device/google/cuttlefish/host/commands/assemble_cvd/cdisk_spec.pb.h"
namespace {
-constexpr int SECTOR_SIZE = 512;
constexpr int GPT_NUM_PARTITIONS = 128;
-struct __attribute__((packed)) MbrPartitionEntry {
- std::uint8_t status;
- std::uint8_t begin_chs[3];
- std::uint8_t partition_type;
- std::uint8_t end_chs[3];
- std::uint32_t first_lba;
- std::uint32_t num_sectors;
-};
-
-struct __attribute__((packed)) MasterBootRecord {
- std::uint8_t bootstrap_code[446];
- MbrPartitionEntry partitions[4];
- std::uint8_t boot_signature[2];
-};
-
-static_assert(sizeof(MasterBootRecord) == SECTOR_SIZE);
-
/**
* Creates a "Protective" Master Boot Record Partition Table header. The GUID
* Partition Table Specification recommends putting this on the first sector
@@ -195,6 +178,12 @@
next_disk_offset_ += size;
}
+ std::uint64_t DiskSize() const {
+ std::uint64_t align = 1 << 16; // 64k alignment
+ std::uint64_t val = next_disk_offset_ + sizeof(GptEnd);
+ return ((val + (align - 1)) / align) * align;
+ }
+
/**
* Generates a composite disk specification file, assuming that `header_file`
* and `footer_file` will be populated with the contents of `Beginning()` and
@@ -204,7 +193,7 @@
const std::string& footer_file) const {
CompositeDisk disk;
disk.set_version(1);
- disk.set_length(next_disk_offset_ + sizeof(GptEnd));
+ disk.set_length(DiskSize());
ComponentDisk* header = disk.add_component_disks();
header->set_file_path(header_file);
@@ -237,13 +226,13 @@
return {};
}
GptBeginning gpt = {
- .protective_mbr = ProtectiveMbr(next_disk_offset_ + sizeof(GptEnd)),
+ .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,
+ .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,
@@ -306,9 +295,10 @@
return true;
}
-bool WriteEnd(cvd::SharedFD out, const GptEnd& end) {
- std::string begin_str((const char*) &end, sizeof(GptEnd));
- if (cvd::WriteAll(out, begin_str) != begin_str.size()) {
+bool WriteEnd(cvd::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 (cvd::WriteAll(out, end_str) != end_str.size()) {
LOG(ERROR) << "Could not write GPT end: " << out->StrError();
return false;
}
@@ -386,7 +376,9 @@
<< "\" to \"" << output_path << "\": " << output->StrError();
}
}
- if (!WriteEnd(output, builder.End(beginning))) {
+ std::uint64_t padding =
+ builder.DiskSize() - ((beginning.header.backup_lba + 1) * SECTOR_SIZE);
+ if (!WriteEnd(output, builder.End(beginning), padding)) {
LOG(FATAL) << "Could not write GPT end to \"" << output_path
<< "\": " << output->StrError();
}
@@ -407,7 +399,9 @@
<< "\": " << header->StrError();
}
auto footer = cvd::SharedFD::Creat(footer_file, 0600);
- if (!WriteEnd(footer, builder.End(beginning))) {
+ std::uint64_t padding =
+ builder.DiskSize() - ((beginning.header.backup_lba + 1) * SECTOR_SIZE);
+ if (!WriteEnd(footer, builder.End(beginning), padding)) {
LOG(FATAL) << "Could not write GPT end to \"" << footer_file
<< "\": " << footer->StrError();
}
diff --git a/host/commands/assemble_cvd/mbr.h b/host/commands/assemble_cvd/mbr.h
new file mode 100644
index 0000000..b06a5e3
--- /dev/null
+++ b/host/commands/assemble_cvd/mbr.h
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+constexpr int SECTOR_SIZE = 512;
+
+struct __attribute__((packed)) MbrPartitionEntry {
+ std::uint8_t status;
+ std::uint8_t begin_chs[3];
+ std::uint8_t partition_type;
+ std::uint8_t end_chs[3];
+ std::uint32_t first_lba;
+ std::uint32_t num_sectors;
+};
+
+struct __attribute__((packed)) MasterBootRecord {
+ std::uint8_t bootstrap_code[446];
+ MbrPartitionEntry partitions[4];
+ std::uint8_t boot_signature[2];
+};
+
+static_assert(sizeof(MasterBootRecord) == SECTOR_SIZE);
diff --git a/host/commands/assemble_cvd/super_image_mixer.cc b/host/commands/assemble_cvd/super_image_mixer.cc
index bdcec93..b05f72f 100644
--- a/host/commands/assemble_cvd/super_image_mixer.cc
+++ b/host/commands/assemble_cvd/super_image_mixer.cc
@@ -61,6 +61,7 @@
"IMAGES/odm.img",
"IMAGES/recovery.img",
"IMAGES/userdata.img",
+ "IMAGES/vbmeta.img",
"IMAGES/vendor.img",
};
diff --git a/host/commands/fetcher/build_api.cc b/host/commands/fetcher/build_api.cc
index 2722de8..a83df58 100644
--- a/host/commands/fetcher/build_api.cc
+++ b/host/commands/fetcher/build_api.cc
@@ -72,6 +72,12 @@
return out;
}
+DirectoryBuild::DirectoryBuild(const std::vector<std::string>& paths,
+ const std::string& target)
+ : paths(paths), target(target), id("eng") {
+ product = getenv("TARGET_PRODUCT");
+}
+
BuildApi::BuildApi(std::unique_ptr<CredentialSource> credential_source)
: credential_source(std::move(credential_source)) {}
@@ -110,6 +116,15 @@
return response_json["buildAttemptStatus"].asString();
}
+std::string BuildApi::ProductName(const DeviceBuild& build) {
+ std::string url = BUILD_API + "/builds/" + build.id + "/" + build.target;
+ auto response_json = curl.DownloadToJson(url, Headers());
+ CHECK(!response_json.isMember("error")) << "Error fetching the status of "
+ << "build " << build << ". Response was " << response_json;
+ CHECK(response_json.isMember("target")) << "Build was missing target field.";
+ return response_json["target"]["product"].asString();
+}
+
std::vector<Artifact> BuildApi::Artifacts(const DeviceBuild& build) {
std::string url = BUILD_API + "/builds/" + build.id + "/" + build.target
+ "/attempts/latest/artifacts?maxResults=1000";
@@ -213,5 +228,6 @@
status = build_api->BuildStatus(proposed_build);
}
LOG(INFO) << "Status for build " << proposed_build << " is " << status;
+ proposed_build.product = build_api->ProductName(proposed_build);
return proposed_build;
}
diff --git a/host/commands/fetcher/build_api.h b/host/commands/fetcher/build_api.h
index 54bbd72..8ccf8b2 100644
--- a/host/commands/fetcher/build_api.h
+++ b/host/commands/fetcher/build_api.h
@@ -56,6 +56,7 @@
std::string id;
std::string target;
+ std::string product;
};
std::ostream& operator<<(std::ostream&, const DeviceBuild&);
@@ -63,12 +64,12 @@
struct DirectoryBuild {
// TODO(schuffelen): Support local builds other than "eng"
DirectoryBuild(const std::vector<std::string>& paths,
- const std::string& target)
- : paths(paths), target(target), id("eng") {}
+ const std::string& target);
std::vector<std::string> paths;
std::string target;
std::string id;
+ std::string product;
};
std::ostream& operator<<(std::ostream&, const DirectoryBuild&);
@@ -91,6 +92,8 @@
std::string BuildStatus(const DeviceBuild&);
+ std::string ProductName(const DeviceBuild&);
+
std::vector<Artifact> Artifacts(const DeviceBuild&);
bool ArtifactToFile(const DeviceBuild& build, const std::string& artifact,
diff --git a/host/commands/fetcher/fetch_cvd.cc b/host/commands/fetcher/fetch_cvd.cc
index 42ce875..8b1387a 100644
--- a/host/commands/fetcher/fetch_cvd.cc
+++ b/host/commands/fetcher/fetch_cvd.cc
@@ -72,13 +72,9 @@
std::string TargetBuildZipFromArtifacts(
const Build& build, const std::string& name,
const std::vector<Artifact>& artifacts) {
- std::string target = std::visit([](auto&& arg) { return arg.target; }, build);
- size_t dash_pos = target.find('-');
- if (dash_pos != std::string::npos) {
- target.replace(dash_pos, target.size() - dash_pos, "");
- }
+ std::string product = std::visit([](auto&& arg) { return arg.product; }, build);
auto id = std::visit([](auto&& arg) { return arg.id; }, build);
- auto match = target + "-" + name + "-" + id;
+ auto match = product + "-" + name + "-" + id;
for (const auto& artifact : artifacts) {
if (artifact.Name().find(match) != std::string::npos) {
return artifact.Name();
@@ -322,11 +318,12 @@
bool system_in_img_zip = true;
if (FLAGS_download_img_zip) {
std::vector<std::string> image_files =
- download_images(&build_api, system_build, target_dir, {"system.img"});
+ download_images(&build_api, system_build, target_dir,
+ {"system.img", "product.img"});
if (image_files.empty()) {
LOG(INFO) << "Could not find system image for " << system_build
<< "in the img zip. Assuming a super image build, which will "
- << "get the super image from the target zip.";
+ << "get the system image from the target zip.";
system_in_img_zip = false;
} else {
LOG(INFO) << "Adding img-zip files for system build";
@@ -368,6 +365,28 @@
<< strerror(error_num);
return -1;
}
+ if (ExtractImages(target_files[0], target_dir, {"IMAGES/system_ext.img"})
+ != std::vector<std::string>{}) {
+ std::string extracted_system_ext = target_dir + "/IMAGES/system_ext.img";
+ std::string target_system_ext = target_dir + "/system_ext.img";
+ if (rename(extracted_system_ext.c_str(), target_system_ext.c_str())) {
+ int error_num = errno;
+ LOG(FATAL) << "Could not move system_ext.img in target directory: "
+ << strerror(error_num);
+ return -1;
+ }
+ }
+ if (ExtractImages(target_files[0], target_dir, {"IMAGES/vbmeta_system.img"})
+ != std::vector<std::string>{}) {
+ std::string extracted_vbmeta_system = target_dir + "/IMAGES/vbmeta_system.img";
+ std::string target_vbmeta_system = target_dir + "/vbmeta_system.img";
+ if (rename(extracted_vbmeta_system.c_str(), target_vbmeta_system.c_str())) {
+ int error_num = errno;
+ LOG(FATAL) << "Could not move vbmeta_system.img in target directory: "
+ << strerror(error_num);
+ return -1;
+ }
+ }
// This should technically call AddFilesToConfig with the produced files,
// but it will conflict with the ones produced from the default system image
// and pie doesn't care about the produced file list anyway.
diff --git a/host/commands/launch/Android.bp b/host/commands/launch/Android.bp
index 53ed2c3..548931e 100644
--- a/host/commands/launch/Android.bp
+++ b/host/commands/launch/Android.bp
@@ -33,5 +33,8 @@
"libxml2",
"libjsoncpp",
],
+ required: [
+ "mkenvimage",
+ ],
defaults: ["cuttlefish_host_only", "cuttlefish_libicuuc"],
}
diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc
index abf1f6f..e624e7c 100644
--- a/host/commands/run_cvd/launch.cc
+++ b/host/commands/run_cvd/launch.cc
@@ -1,20 +1,20 @@
#include "host/commands/run_cvd/launch.h"
-#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <android-base/logging.h>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/files.h"
#include "common/libs/utils/size_utils.h"
-#include "host/commands/run_cvd/runner_defs.h"
#include "host/commands/run_cvd/pre_launch_initializers.h"
+#include "host/commands/run_cvd/runner_defs.h"
#include "host/libs/vm_manager/crosvm_manager.h"
#include "host/libs/vm_manager/qemu_manager.h"
-using cvd::RunnerExitCodes;
using cvd::MonitorEntry;
+using cvd::RunnerExitCodes;
namespace {
@@ -25,9 +25,8 @@
std::string GetAdbConnectorVsockArg(const vsoc::CuttlefishConfig& config) {
auto instance = config.ForDefaultInstance();
- return std::string{"vsock:"}
- + std::to_string(instance.vsock_guest_cid())
- + std::string{":5555"};
+ return std::string{"vsock:"} + std::to_string(instance.vsock_guest_cid()) +
+ std::string{":5555"};
}
bool AdbModeEnabled(const vsoc::CuttlefishConfig& config, vsoc::AdbMode mode) {
@@ -36,14 +35,14 @@
bool AdbVsockTunnelEnabled(const vsoc::CuttlefishConfig& config) {
auto instance = config.ForDefaultInstance();
- return instance.vsock_guest_cid() > 2
- && AdbModeEnabled(config, vsoc::AdbMode::VsockTunnel);
+ return instance.vsock_guest_cid() > 2 &&
+ AdbModeEnabled(config, vsoc::AdbMode::VsockTunnel);
}
bool AdbVsockHalfTunnelEnabled(const vsoc::CuttlefishConfig& config) {
auto instance = config.ForDefaultInstance();
- return instance.vsock_guest_cid() > 2
- && AdbModeEnabled(config, vsoc::AdbMode::VsockHalfTunnel);
+ return instance.vsock_guest_cid() > 2 &&
+ AdbModeEnabled(config, vsoc::AdbMode::VsockHalfTunnel);
}
bool AdbTcpConnectorEnabled(const vsoc::CuttlefishConfig& config) {
@@ -53,8 +52,8 @@
}
bool AdbVsockConnectorEnabled(const vsoc::CuttlefishConfig& config) {
- return config.run_adb_connector()
- && AdbModeEnabled(config, vsoc::AdbMode::NativeVsock);
+ return config.run_adb_connector() &&
+ AdbModeEnabled(config, vsoc::AdbMode::NativeVsock);
}
cvd::OnSocketReadyCb GetOnSubprocessExitCallback(
@@ -67,10 +66,10 @@
}
cvd::SharedFD CreateUnixInputServer(const std::string& path) {
- auto server = cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
+ auto server =
+ cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
if (!server->IsOpen()) {
- LOG(ERROR) << "Unable to create unix input server: "
- << server->StrError();
+ LOG(ERROR) << "Unable to create unix input server: " << server->StrError();
return cvd::SharedFD();
}
return server;
@@ -78,8 +77,8 @@
// Creates the frame and input sockets and add the relevant arguments to the vnc
// server and webrtc commands
-StreamerLaunchResult CreateStreamerServers(cvd::Command* cmd,
- const vsoc::CuttlefishConfig& config) {
+StreamerLaunchResult CreateStreamerServers(
+ cvd::Command* cmd, const vsoc::CuttlefishConfig& config) {
StreamerLaunchResult server_ret;
cvd::SharedFD touch_server;
cvd::SharedFD keyboard_server;
@@ -104,7 +103,8 @@
cmd->AddParameter("-touch_fd=", touch_server);
if (!keyboard_server->IsOpen()) {
- LOG(ERROR) << "Could not open keyboard server: " << keyboard_server->StrError();
+ LOG(ERROR) << "Could not open keyboard server: "
+ << keyboard_server->StrError();
return {};
}
cmd->AddParameter("-keyboard_fd=", keyboard_server);
@@ -125,15 +125,14 @@
return server_ret;
}
-} // namespace
+} // namespace
bool LogcatReceiverEnabled(const vsoc::CuttlefishConfig& config) {
return config.logcat_mode() == cvd::kLogcatVsockMode;
}
std::vector<cvd::SharedFD> LaunchKernelLogMonitor(
- const vsoc::CuttlefishConfig& config,
- cvd::ProcessMonitor* process_monitor,
+ const vsoc::CuttlefishConfig& config, cvd::ProcessMonitor* process_monitor,
unsigned int number_of_event_pipes) {
auto instance = config.ForDefaultInstance();
auto log_name = instance.kernel_log_pipe_name();
@@ -251,8 +250,7 @@
}
StreamerLaunchResult LaunchVNCServer(
- const vsoc::CuttlefishConfig& config,
- cvd::ProcessMonitor* process_monitor,
+ const vsoc::CuttlefishConfig& config, cvd::ProcessMonitor* process_monitor,
std::function<bool(MonitorEntry*)> callback) {
auto instance = config.ForDefaultInstance();
// Launch the vnc server, don't wait for it to complete
@@ -295,23 +293,36 @@
StreamerLaunchResult LaunchWebRTC(cvd::ProcessMonitor* process_monitor,
const vsoc::CuttlefishConfig& config) {
- cvd::Command webrtc(config.webrtc_binary());
-
- if (!config.webrtc_certs_dir().empty()) {
- webrtc.AddParameter("--certs_dir=", config.webrtc_certs_dir());
+ if (config.ForDefaultInstance().start_webrtc_sig_server()) {
+ cvd::Command sig_server(config.sig_server_binary());
+ 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->StartSubprocess(std::move(sig_server),
+ GetOnSubprocessExitCallback(config));
}
- webrtc.AddParameter("--http_server_port=", vsoc::ForCurrentInstance(8443));
- webrtc.AddParameter("--public_ip=", config.webrtc_public_ip());
- webrtc.AddParameter("--assets_dir=", config.webrtc_assets_dir());
+ // 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.
+
+ cvd::Command webrtc(config.webrtc_binary());
+ webrtc.AddParameter("-public_ip=", config.webrtc_public_ip());
auto server_ret = CreateStreamerServers(&webrtc, config);
if (config.webrtc_enable_adb_websocket()) {
- auto instance = config.ForDefaultInstance();
- webrtc.AddParameter("--adb=", instance.adb_ip_and_port());
+ auto instance = config.ForDefaultInstance();
+ webrtc.AddParameter("--adb=", instance.adb_ip_and_port());
}
+ // TODO get from launcher params
process_monitor->StartSubprocess(std::move(webrtc),
GetOnSubprocessExitCallback(config));
server_ret.launched = true;
@@ -320,14 +331,14 @@
}
void LaunchSocketVsockProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
- const vsoc::CuttlefishConfig& config) {
+ const vsoc::CuttlefishConfig& config) {
auto instance = config.ForDefaultInstance();
if (AdbVsockTunnelEnabled(config)) {
cvd::Command adb_tunnel(config.socket_vsock_proxy_binary());
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{"--tcp_port="} +
+ std::to_string(instance.host_port()));
adb_tunnel.AddParameter(std::string{"--vsock_cid="} +
std::to_string(instance.vsock_guest_cid()));
process_monitor->StartSubprocess(std::move(adb_tunnel),
@@ -337,8 +348,8 @@
cvd::Command adb_tunnel(config.socket_vsock_proxy_binary());
adb_tunnel.AddParameter("--server=tcp");
adb_tunnel.AddParameter("--vsock_port=5555");
- adb_tunnel.AddParameter(
- std::string{"--tcp_port="} + std::to_string(instance.host_port()));
+ 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->StartSubprocess(std::move(adb_tunnel),
@@ -366,7 +377,7 @@
}
void LaunchMetrics(cvd::ProcessMonitor* process_monitor,
- const vsoc::CuttlefishConfig& config) {
+ const vsoc::CuttlefishConfig& config) {
cvd::Command metrics(config.metrics_binary());
process_monitor->StartSubprocess(std::move(metrics),
@@ -394,10 +405,21 @@
const vsoc::CuttlefishConfig& config) {
if (config.tpm_device() != "") {
if (config.tpm_binary() != "") {
- LOG(WARNING) << "Both -tpm_device and -tpm_binary were set. Using -tpm_device.";
+ LOG(WARNING)
+ << "Both -tpm_device and -tpm_binary were set. Using -tpm_device.";
}
LaunchTpmPassthrough(process_monitor, config);
} else if (config.tpm_binary() != "") {
LaunchTpmSimulator(process_monitor, config);
}
}
+
+void LaunchSecureEnvironment(cvd::ProcessMonitor* process_monitor,
+ const vsoc::CuttlefishConfig& config) {
+ auto port = config.ForDefaultInstance().keymaster_vsock_port();
+ auto server = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
+ cvd::Command command(vsoc::DefaultHostArtifactsPath("bin/secure_env"));
+ command.AddParameter("-keymaster_fd=", server);
+ process_monitor->StartSubprocess(std::move(command),
+ GetOnSubprocessExitCallback(config));
+}
diff --git a/host/commands/run_cvd/launch.h b/host/commands/run_cvd/launch.h
index af3ec40..650886b 100644
--- a/host/commands/run_cvd/launch.h
+++ b/host/commands/run_cvd/launch.h
@@ -42,3 +42,6 @@
void LaunchMetrics(cvd::ProcessMonitor* process_monitor,
const vsoc::CuttlefishConfig& config);
+
+void LaunchSecureEnvironment(cvd::ProcessMonitor* process_monitor,
+ const vsoc::CuttlefishConfig& config);
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index c72237e..5cd2b81 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -424,6 +424,7 @@
LaunchConfigServer(*config, &process_monitor);
LaunchTombstoneReceiverIfEnabled(*config, &process_monitor);
LaunchTpm(&process_monitor, *config);
+ LaunchSecureEnvironment(&process_monitor, *config);
// The streamer needs to launch before the VMM because it serves on several
// sockets (input devices, vsock frame server) when using crosvm.
diff --git a/host/commands/secure_env/Android.bp b/host/commands/secure_env/Android.bp
new file mode 100644
index 0000000..8d8efe7
--- /dev/null
+++ b/host/commands/secure_env/Android.bp
@@ -0,0 +1,42 @@
+//
+// 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.
+
+cc_binary_host {
+ name: "secure_env",
+ srcs: [
+ "keymaster_responder.cpp",
+ "secure_env.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcuttlefish_fs",
+ "libcuttlefish_security",
+ "libcuttlefish_utils",
+ "libkeymaster_portable",
+ "libkeymaster_messages",
+ "libsoft_attestation_cert",
+ "liblog",
+ "libcrypto",
+ "libcutils",
+ "libpuresoftkeymasterdevice_host",
+ ],
+ static_libs: [
+ "libgflags",
+ ],
+ defaults: ["cuttlefish_host_only"],
+ cflags: [
+ "-fno-rtti", // Required for libkeymaster_portable
+ ],
+}
diff --git a/host/commands/secure_env/keymaster_responder.cpp b/host/commands/secure_env/keymaster_responder.cpp
new file mode 100644
index 0000000..2076866
--- /dev/null
+++ b/host/commands/secure_env/keymaster_responder.cpp
@@ -0,0 +1,104 @@
+//
+// 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 "keymaster_responder.h"
+
+#include <android-base/logging.h>
+#include <keymaster/android_keymaster_messages.h>
+
+KeymasterResponder::KeymasterResponder(
+ cvd::KeymasterChannel* channel, keymaster::AndroidKeymaster* keymaster)
+ : channel_(channel), keymaster_(keymaster) {
+}
+
+bool KeymasterResponder::ProcessMessage() {
+ auto request = channel_->ReceiveMessage();
+ if (!request) {
+ LOG(ERROR) << "Could not receive message";
+ return false;
+ }
+ const uint8_t* buffer = request->payload;
+ const uint8_t* end = request->payload + request->payload_size;
+ switch(request->cmd) {
+ using namespace keymaster;
+#define HANDLE_MESSAGE(ENUM_NAME, METHOD_NAME) \
+ case ENUM_NAME: {\
+ METHOD_NAME##Request request; \
+ if (!request.Deserialize(&buffer, end)) { \
+ LOG(ERROR) << "Failed to deserialize " #METHOD_NAME "Request"; \
+ return false; \
+ } \
+ METHOD_NAME##Response response; \
+ keymaster_->METHOD_NAME(request, &response); \
+ return channel_->SendResponse(ENUM_NAME, response); \
+ }
+ HANDLE_MESSAGE(GENERATE_KEY, GenerateKey)
+ HANDLE_MESSAGE(BEGIN_OPERATION, BeginOperation)
+ HANDLE_MESSAGE(UPDATE_OPERATION, UpdateOperation)
+ HANDLE_MESSAGE(FINISH_OPERATION, FinishOperation)
+ HANDLE_MESSAGE(ABORT_OPERATION, AbortOperation)
+ HANDLE_MESSAGE(IMPORT_KEY, ImportKey)
+ HANDLE_MESSAGE(EXPORT_KEY, ExportKey)
+ HANDLE_MESSAGE(GET_VERSION, GetVersion)
+ HANDLE_MESSAGE(GET_SUPPORTED_ALGORITHMS, SupportedAlgorithms)
+ HANDLE_MESSAGE(GET_SUPPORTED_BLOCK_MODES, SupportedBlockModes)
+ HANDLE_MESSAGE(GET_SUPPORTED_PADDING_MODES, SupportedPaddingModes)
+ HANDLE_MESSAGE(GET_SUPPORTED_DIGESTS, SupportedDigests)
+ HANDLE_MESSAGE(GET_SUPPORTED_IMPORT_FORMATS, SupportedImportFormats)
+ HANDLE_MESSAGE(GET_SUPPORTED_EXPORT_FORMATS, SupportedExportFormats)
+ HANDLE_MESSAGE(GET_KEY_CHARACTERISTICS, GetKeyCharacteristics)
+ HANDLE_MESSAGE(ATTEST_KEY, AttestKey)
+ HANDLE_MESSAGE(UPGRADE_KEY, UpgradeKey)
+ HANDLE_MESSAGE(CONFIGURE, Configure)
+ HANDLE_MESSAGE(DELETE_KEY, DeleteKey)
+ HANDLE_MESSAGE(DELETE_ALL_KEYS, DeleteAllKeys)
+ HANDLE_MESSAGE(IMPORT_WRAPPED_KEY, ImportWrappedKey)
+#undef HANDLE_MESSAGE
+#define HANDLE_MESSAGE(ENUM_NAME, METHOD_NAME) \
+ case ENUM_NAME: {\
+ METHOD_NAME##Request request; \
+ if (!request.Deserialize(&buffer, end)) { \
+ LOG(ERROR) << "Failed to deserialize " #METHOD_NAME "Request"; \
+ return false; \
+ } \
+ auto response = keymaster_->METHOD_NAME(request); \
+ return channel_->SendResponse(ENUM_NAME, response); \
+ }
+ HANDLE_MESSAGE(COMPUTE_SHARED_HMAC, ComputeSharedHmac)
+ HANDLE_MESSAGE(VERIFY_AUTHORIZATION, VerifyAuthorization)
+#undef HANDLE_MESSAGE
+#define HANDLE_MESSAGE(ENUM_NAME, METHOD_NAME) \
+ case ENUM_NAME: {\
+ auto response = keymaster_->METHOD_NAME(); \
+ return channel_->SendResponse(ENUM_NAME, response); \
+ }
+ HANDLE_MESSAGE(GET_HMAC_SHARING_PARAMETERS, GetHmacSharingParameters)
+ HANDLE_MESSAGE(EARLY_BOOT_ENDED, EarlyBootEnded)
+ case ADD_RNG_ENTROPY: {
+ AddEntropyRequest request;
+ if (!request.Deserialize(&buffer, end)) {
+ LOG(ERROR) << "Failed to deserialize AddEntropyRequest";
+ return false;
+ }
+ AddEntropyResponse response;
+ keymaster_->AddRngEntropy(request, &response);
+ return channel_->SendResponse(ADD_RNG_ENTROPY, response);
+ }
+ case DESTROY_ATTESTATION_IDS: // Not defined in AndroidKeymaster?
+ break;
+ }
+ LOG(ERROR) << "Unknown request type: " << request->cmd;
+ return false;
+}
diff --git a/host/commands/secure_env/keymaster_responder.h b/host/commands/secure_env/keymaster_responder.h
new file mode 100644
index 0000000..b30c6a4
--- /dev/null
+++ b/host/commands/secure_env/keymaster_responder.h
@@ -0,0 +1,31 @@
+//
+// 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 <keymaster/android_keymaster.h>
+
+#include "common/libs/security/keymaster_channel.h"
+
+class KeymasterResponder {
+private:
+ cvd::KeymasterChannel* channel_;
+ keymaster::AndroidKeymaster* keymaster_;
+public:
+ KeymasterResponder(cvd::KeymasterChannel* channel,
+ keymaster::AndroidKeymaster* keymaster);
+
+ bool ProcessMessage();
+};
diff --git a/host/commands/secure_env/secure_env.cpp b/host/commands/secure_env/secure_env.cpp
new file mode 100644
index 0000000..c9edc6b
--- /dev/null
+++ b/host/commands/secure_env/secure_env.cpp
@@ -0,0 +1,51 @@
+//
+// 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/logging.h>
+#include <gflags/gflags.h>
+#include <keymaster/android_keymaster.h>
+#include <keymaster/contexts/pure_soft_keymaster_context.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/security/keymaster_channel.h"
+#include "host/commands/secure_env/keymaster_responder.h"
+
+// Copied from AndroidKeymaster4Device
+constexpr size_t kOperationTableSize = 16;
+
+DEFINE_int32(keymaster_fd, -1, "A file descriptor for keymaster communication");
+
+int main(int argc, char** argv) {
+ ::android::base::InitLogging(argv);
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ keymaster::PureSoftKeymasterContext keymaster_context{
+ KM_SECURITY_LEVEL_SOFTWARE};
+ keymaster::AndroidKeymaster keymaster{&keymaster_context, kOperationTableSize};
+
+ CHECK(FLAGS_keymaster_fd != -1)
+ << "TODO(schuffelen): Add keymaster_fd alternative";
+ auto server = cvd::SharedFD::Dup(FLAGS_keymaster_fd);
+ CHECK(server->IsOpen()) << "Could not dup server fd: " << server->StrError();
+ close(FLAGS_keymaster_fd);
+ auto conn = cvd::SharedFD::Accept(*server);
+ CHECK(conn->IsOpen()) << "Unable to open connection: " << conn->StrError();
+ cvd::KeymasterChannel keymaster_channel(conn);
+
+ KeymasterResponder keymaster_responder(&keymaster_channel, &keymaster);
+
+ // TODO(schuffelen): Do this in a thread when adding other HALs
+ while (keymaster_responder.ProcessMessage()) {
+ }
+}
diff --git a/host/commands/tapsetiff/Android.bp b/host/commands/tapsetiff/Android.bp
new file mode 100644
index 0000000..c860241
--- /dev/null
+++ b/host/commands/tapsetiff/Android.bp
@@ -0,0 +1,19 @@
+//
+// 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.
+
+sh_binary_host {
+ name: "tapsetiff",
+ src: "tapsetiff.py",
+}
diff --git a/host/commands/tapsetiff/tapsetiff.py b/host/commands/tapsetiff/tapsetiff.py
new file mode 100755
index 0000000..3e7c9e4
--- /dev/null
+++ b/host/commands/tapsetiff/tapsetiff.py
@@ -0,0 +1,30 @@
+#!/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/tpm_simulator_manager/tpm_simulator_manager.cpp b/host/commands/tpm_simulator_manager/tpm_simulator_manager.cpp
index cd726f3..c5d8225 100644
--- a/host/commands/tpm_simulator_manager/tpm_simulator_manager.cpp
+++ b/host/commands/tpm_simulator_manager/tpm_simulator_manager.cpp
@@ -87,15 +87,18 @@
}
if (command_server && platform_server && !sent_init) {
client = cvd::SharedFD::SocketLocalClient(FLAGS_port + 1, SOCK_STREAM);
- std::vector<char> command_bytes(4, 0);
- *reinterpret_cast<std::uint32_t*>(command_bytes.data()) = htobe32(1); // TPM_SIGNAL_POWER_ON
- CHECK(cvd::WriteAll(client, command_bytes) == 4) << "Could not send TPM_SIGNAL_POWER_ON";
- std::vector<char> response_bytes(4, 0);
- CHECK(cvd::ReadExact(client, &response_bytes) == 4) << "Could not read parity response";
+ std::uint32_t command = htobe32(1); // TPM_SIGNAL_POWER_ON
+ CHECK(cvd::WriteAllBinary(client, &command) == 4)
+ << "Could not send TPM_SIGNAL_POWER_ON";
+ std::uint32_t response;
+ CHECK(cvd::ReadExactBinary(client, &response) == 4)
+ << "Could not read parity response";
- *reinterpret_cast<std::uint32_t*>(command_bytes.data()) = htobe32(11); // TPM_SIGNAL_NV_ON
- CHECK(cvd::WriteAll(client, command_bytes) == 4) << "Could not send TPM_SIGNAL_NV_ON";
- CHECK(cvd::ReadExact(client, &response_bytes) == 4) << "Could not read parity response";
+ command = htobe32(11); // TPM_SIGNAL_NV_ON
+ CHECK(cvd::WriteAllBinary(client, &command) == 4)
+ << "Could not send TPM_SIGNAL_NV_ON";
+ CHECK(cvd::ReadExactBinary(client, &response) == 4)
+ << "Could not read parity response";
sent_init = true;
}
diff --git a/host/frontend/gcastv2/https/Android.bp b/host/frontend/gcastv2/https/Android.bp
index 4078e80..1a290d5 100644
--- a/host/frontend/gcastv2/https/Android.bp
+++ b/host/frontend/gcastv2/https/Android.bp
@@ -35,9 +35,9 @@
"libbase",
"libcrypto",
"libssl",
- "liblog",
+ "liblog",
+ "libcuttlefish_utils",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
-
diff --git a/host/frontend/gcastv2/https/HTTPServer.cpp b/host/frontend/gcastv2/https/HTTPServer.cpp
index 967f5f7..6c4a5ff 100644
--- a/host/frontend/gcastv2/https/HTTPServer.cpp
+++ b/host/frontend/gcastv2/https/HTTPServer.cpp
@@ -19,6 +19,7 @@
#include <https/ClientSocket.h>
#include <https/HTTPRequestResponse.h>
#include <https/Support.h>
+#include "common/libs/utils/base64.h"
#include <android-base/logging.h>
@@ -298,7 +299,7 @@
CHECK_EQ(res, 1);
std::string acceptKey;
- encodeBase64(digest, sizeof(digest), &acceptKey);
+ cvd::EncodeBase64(digest, sizeof(digest), &acceptKey);
(*responseHeaders)["Sec-WebSocket-Accept"] = acceptKey;
diff --git a/host/frontend/gcastv2/https/PlainSocket.cpp b/host/frontend/gcastv2/https/PlainSocket.cpp
index 26ee574..6a870f3 100644
--- a/host/frontend/gcastv2/https/PlainSocket.cpp
+++ b/host/frontend/gcastv2/https/PlainSocket.cpp
@@ -44,9 +44,9 @@
const sockaddr *addr,
socklen_t addrLen) {
if (!addr) {
- return ::send(fd(), data, size, 0);
+ return ::send(fd(), data, size, MSG_NOSIGNAL);
}
- return ::sendto(fd(), data, size, 0, addr, addrLen);
+ return ::sendto(fd(), data, size, MSG_NOSIGNAL, addr, addrLen);
}
void PlainSocket::postFlush(RunLoop::AsyncFunction fn) {
diff --git a/host/frontend/gcastv2/https/SSLSocket.cpp b/host/frontend/gcastv2/https/SSLSocket.cpp
index db8543a..e86df23 100644
--- a/host/frontend/gcastv2/https/SSLSocket.cpp
+++ b/host/frontend/gcastv2/https/SSLSocket.cpp
@@ -362,7 +362,7 @@
while (offset < size) {
ssize_t n = ::send(
- fd(), mOutBuffer.data() + offset, size - offset, 0);
+ fd(), mOutBuffer.data() + offset, size - offset, MSG_NOSIGNAL);
if (n < 0) {
if (errno == EINTR) {
diff --git a/host/frontend/gcastv2/https/Support.cpp b/host/frontend/gcastv2/https/Support.cpp
index be9bcf5..30a0021 100644
--- a/host/frontend/gcastv2/https/Support.cpp
+++ b/host/frontend/gcastv2/https/Support.cpp
@@ -71,53 +71,6 @@
return ss.str();
}
-static char encode6Bit(unsigned x) {
- static char base64[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- return base64[x & 63];
-}
-
-void encodeBase64(const void *_data, size_t size, std::string *out) {
- out->clear();
- out->reserve(((size+2)/3)*4);
-
- const uint8_t *data = (const uint8_t *)_data;
-
- size_t i;
- for (i = 0; i < (size / 3) * 3; i += 3) {
- uint8_t x1 = data[i];
- uint8_t x2 = data[i + 1];
- uint8_t x3 = data[i + 2];
-
- out->append(1, encode6Bit(x1 >> 2));
- out->append(1, encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
- out->append(1, encode6Bit((x2 << 2 | x3 >> 6) & 0x3f));
- out->append(1, encode6Bit(x3 & 0x3f));
- }
- switch (size % 3) {
- case 0:
- break;
- case 2:
- {
- uint8_t x1 = data[i];
- uint8_t x2 = data[i + 1];
- out->append(1, encode6Bit(x1 >> 2));
- out->append(1, encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
- out->append(1, encode6Bit((x2 << 2) & 0x3f));
- out->append(1, '=');
- break;
- }
- default:
- {
- uint8_t x1 = data[i];
- out->append(1, encode6Bit(x1 >> 2));
- out->append(1, encode6Bit((x1 << 4) & 0x3f));
- out->append("==");
- break;
- }
- }
-}
-
uint16_t U16_AT(const uint8_t *ptr) {
return ptr[0] << 8 | ptr[1];
}
diff --git a/host/frontend/gcastv2/https/WebSocketHandler.cpp b/host/frontend/gcastv2/https/WebSocketHandler.cpp
index c8b5526..3776a24 100644
--- a/host/frontend/gcastv2/https/WebSocketHandler.cpp
+++ b/host/frontend/gcastv2/https/WebSocketHandler.cpp
@@ -82,6 +82,8 @@
uint8_t opcode = headerByte & 0x0f;
if (opcode == 0x9 /*ping*/) {
sendMessage(&packet[packetOffset], payloadLen, SendMode::pong);
+ } else if (opcode == 0x8 /*close*/) {
+ return -1;
}
} else {
err = handleMessage(headerByte, &packet[packetOffset], payloadLen);
diff --git a/host/frontend/gcastv2/https/include/https/Support.h b/host/frontend/gcastv2/https/include/https/Support.h
index 63c5999..a829fe5 100644
--- a/host/frontend/gcastv2/https/include/https/Support.h
+++ b/host/frontend/gcastv2/https/include/https/Support.h
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <string>
+#include <vector>
#ifdef NDEBUG
#define DEBUG_ONLY(x)
@@ -29,8 +30,6 @@
void makeFdNonblocking(int fd);
std::string hexdump(const void *_data, size_t size);
-void encodeBase64(const void *_data, size_t size, std::string *out);
-
uint16_t U16_AT(const uint8_t *ptr);
uint32_t U32_AT(const uint8_t *ptr);
uint64_t U64_AT(const uint8_t *ptr);
diff --git a/host/frontend/gcastv2/signaling_server/Android.bp b/host/frontend/gcastv2/signaling_server/Android.bp
new file mode 100644
index 0000000..cf441da
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/Android.bp
@@ -0,0 +1,117 @@
+//
+// 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.
+
+cc_library_headers {
+ name: "webrtc_signaling_headers",
+ export_include_dirs: ["./constants"],
+ host_supported: true,
+}
+
+cc_binary_host {
+ name: "webrtc_sig_server",
+ srcs: [
+ "client_handler.cpp",
+ "device_registry.cpp",
+ "device_handler.cpp",
+ "device_list_handler.cpp",
+ "server_config.cpp",
+ "server.cpp",
+ "signal_handler.cpp",
+ ],
+ header_libs: [
+ "webrtc_signaling_headers",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libssl",
+ "libcuttlefish_fs",
+ ],
+ static_libs: [
+ "libgflags",
+ "libjsoncpp",
+ "libhttps",
+ "libwebrtc",
+ "libcuttlefish_utils",
+ ],
+ defaults: ["cuttlefish_host_only"],
+}
+
+// TODO(jemoreira): Ideally these files should be in $HOST_OUT/webrtc but I
+// couldn't find a module type that would produce that, prebuilt_usr_share_host
+// is the next best thing for now.
+prebuilt_usr_share_host {
+ name: "webrtc_index.html",
+ src: "assets/index.html",
+ filename: "index.html",
+ sub_dir: "webrtc/assets",
+}
+
+prebuilt_usr_share_host {
+ name: "webrtc_style.css",
+ src: "assets/style.css",
+ filename: "style.css",
+ sub_dir: "webrtc/assets",
+}
+
+prebuilt_usr_share_host {
+ name: "webrtc_logcat.js",
+ src: "assets/js/logcat.js",
+ filename: "logcat.js",
+ sub_dir: "webrtc/assets/js",
+}
+
+prebuilt_usr_share_host {
+ name: "webrtc_cf.js",
+ src: "assets/js/cf_webrtc.js",
+ filename: "cf_webrtc.js",
+ sub_dir: "webrtc/assets/js",
+}
+
+prebuilt_usr_share_host {
+ name: "webrtc_app.js",
+ src: "assets/js/app.js",
+ filename: "app.js",
+ sub_dir: "webrtc/assets/js",
+}
+
+prebuilt_usr_share_host {
+ name: "webrtc_server.crt",
+ src: "certs/server.crt",
+ filename: "server.crt",
+ sub_dir: "webrtc/certs",
+}
+
+prebuilt_usr_share_host {
+ name: "webrtc_server.key",
+ src: "certs/server.key",
+ filename: "server.key",
+ sub_dir: "webrtc/certs",
+}
+
+prebuilt_usr_share_host {
+ name: "webrtc_server.p12",
+ src: "certs/server.p12",
+ filename: "server.p12",
+ sub_dir: "webrtc/certs",
+}
+
+prebuilt_usr_share_host {
+ name: "webrtc_trusted.pem",
+ src: "certs/trusted.pem",
+ filename: "trusted.pem",
+ sub_dir: "webrtc/certs",
+}
diff --git a/host/frontend/gcastv2/signaling_server/Readme.md b/host/frontend/gcastv2/signaling_server/Readme.md
new file mode 100644
index 0000000..8771138
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/Readme.md
@@ -0,0 +1,86 @@
+This signaling server defines a very simple protocol to allow the establishing
+of a WebRTC connection between clients and devices. It should only be used for
+development purposes or for very simple applications with no security, privacy
+or scalability requirements.
+
+Serious applications should build their own signaling server, implementing the
+protocol exactly as defined below (any modifications would likely require
+modifications to the client and/or device which will then not be maintained by
+the cuttlefish team).
+
+The signaling server MUST expose two (different) websocket endpoints:
+
+* wss://<addr>/register_device
+* wss://<addr>/connect_client
+
+Additional endpoints are allowed and are up to the specific applications.
+Extending the messages below with additional fields should be done with extreme
+care, prefixing the field names with an applciation specific word is strongly
+recommended. The same holds true for creating new message types.
+
+Devices connect to the *register_device* endpoint and send these types of
+messages:
+
+* {"message_type": "register", "device_id": <String>, "device_info": <Any>}
+
+* {"message_type": "forward", "client_id": <Integer>, "payload": <Any>}
+
+The server sends the device these types of messages:
+
+* {"message_type": "config", "ice_servers": <Array of IceServer dictionaries>,
+...}
+
+* {"message_type": "client_msg", "client_id": <Integer>, "payload": <Any>}
+
+* {"error": <String>}
+
+Clients connect to the *connect_client* endpoint and send these types of
+messages:
+
+* {"message_type": "connect", "device_id": <String>}
+
+* {"message_type": "forward", "payload": <Any>}
+
+The server sends the clients these types of messages:
+
+* {"message_type": "config", "ice_servers": <Array of IceServer dictionaries>,
+...}
+
+* {"message_type": "device_info", "device_info": <Any>}
+
+* {"message_type": "device_msg", "payload": <Any>}
+
+* {"error": <String>}
+
+A typical application flow looks like this:
+
+* **Device** connects to *register_device*
+
+* **Device** sends **register** message
+
+* **Server** sends **config** message to **Device**
+
+* **Client** connects to *connect_client*
+
+* **Client** sends **connect** message
+
+* **Server** sends **config** message to **Client**
+
+* **Server** sends **device_info** message to **Client**
+
+* **Client** sends **forward** message
+
+* **Server** sends **client_msg** message to **Device** (at this point the
+device knows about the client and cand send **forward** messages intended for
+it)
+
+* **Device** sends **forward** message
+
+* **Server** sends **device_msg** message to client
+
+* ...
+
+In an alternative flow, not supported by this implementation but allowed by the
+design, the **Client** connects first and only receives a **config** message
+from the **Server**, only after the **Device** has sent the **register** message
+the **Server** sends the **device_info** messaage to the **Client**.
diff --git a/host/frontend/gcastv2/webrtc/assets/index.html b/host/frontend/gcastv2/signaling_server/assets/index.html
similarity index 73%
rename from host/frontend/gcastv2/webrtc/assets/index.html
rename to host/frontend/gcastv2/signaling_server/assets/index.html
index 9c8c5a1..ccbd071 100644
--- a/host/frontend/gcastv2/webrtc/assets/index.html
+++ b/host/frontend/gcastv2/signaling_server/assets/index.html
@@ -6,8 +6,13 @@
</head>
<body>
- <button id="receiveButton">Receive Media</button>
+ <section id='device_selector'>
+ <h1>Available devices <span id='refresh_list'>↻</span></h1>
+ <ul id="device_list"></ul>
+ </section>
+ <section id='device_connection'>
<button id="keyboardCaptureBtn">Capture Keyboard</button>
+ <button id="showLogcatBtn">Show Logcat</button>
<hr>
<section class="noscroll">
<div class="one">
@@ -19,6 +24,7 @@
</textarea>
</div>
</section>
+ </section>
<script src="js/logcat.js"></script>
<script src="js/cf_webrtc.js" type="module"></script>
diff --git a/host/frontend/gcastv2/signaling_server/assets/js/app.js b/host/frontend/gcastv2/signaling_server/assets/js/app.js
new file mode 100644
index 0000000..54efbf2
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/assets/js/app.js
@@ -0,0 +1,224 @@
+'use strict';
+
+function ConnectToDevice(device_id, use_tcp) {
+ console.log('ConnectToDevice ', device_id);
+ const keyboardCaptureButton = document.getElementById('keyboardCaptureBtn');
+ keyboardCaptureButton.addEventListener('click', onKeyboardCaptureClick);
+
+ const deviceScreen = document.getElementById('deviceScreen');
+ deviceScreen.addEventListener('click', onInitialClick);
+
+ function onInitialClick(e) {
+ // This stupid thing makes sure that we disable controls after the first
+ // click... Why not just disable controls altogether you ask? Because then
+ // audio won't play because these days user-interaction is required to enable
+ // audio playback...
+ console.log('onInitialClick');
+
+ deviceScreen.controls = false;
+ deviceScreen.removeEventListener('click', onInitialClick);
+ }
+
+ let videoStream;
+ let mouseIsDown = false;
+ let deviceConnection;
+
+ let logcatBtn = document.getElementById('showLogcatBtn');
+ logcatBtn.onclick = ev => {
+ init_logcat(deviceConnection);
+ logcatBtn.remove();
+ };
+
+ let options = {
+ // temporarily disable audio to free ports in the server since it's only
+ // producing silence anyways.
+ disable_audio: true,
+ wsUrl: ((location.protocol == 'http:') ? 'ws://' : 'wss://') +
+ location.host + '/connect_client',
+ use_tcp,
+ };
+ let urlParams = new URLSearchParams(location.search);
+ for (const [key, value] of urlParams) {
+ options[key] = JSON.parse(value);
+ }
+
+ 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;
+ deviceScreen.srcObject = videoStream;
+ }).catch(e => console.error('Unable to get display stream: ', e));
+ startMouseTracking(); // TODO stopMouseTracking() when disconnected
+ });
+
+ function onKeyboardCaptureClick(e) {
+ const selectedClass = 'selected';
+ if (keyboardCaptureButton.classList.contains(selectedClass)) {
+ stopKeyboardTracking();
+ keyboardCaptureButton.classList.remove(selectedClass);
+ } else {
+ startKeyboardTracking();
+ keyboardCaptureButton.classList.add(selectedClass);
+ }
+ }
+
+ function startMouseTracking() {
+ if (window.PointerEvent) {
+ deviceScreen.addEventListener('pointerdown', onStartDrag);
+ deviceScreen.addEventListener('pointermove', onContinueDrag);
+ deviceScreen.addEventListener('pointerup', onEndDrag);
+ } else if (window.TouchEvent) {
+ deviceScreen.addEventListener('touchstart', onStartDrag);
+ deviceScreen.addEventListener('touchmove', onContinueDrag);
+ deviceScreen.addEventListener('touchend', onEndDrag);
+ } else if (window.MouseEvent) {
+ deviceScreen.addEventListener('mousedown', onStartDrag);
+ deviceScreen.addEventListener('mousemove', onContinueDrag);
+ deviceScreen.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);
+ }
+ }
+
+ 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;
+
+ sendMouseUpdate(true, e);
+ }
+
+ function onEndDrag(e) {
+ e.preventDefault();
+
+ // console.log("mouseup at " + e.pageX + " / " + e.pageY);
+ mouseIsDown = false;
+
+ sendMouseUpdate(false, e);
+ }
+
+ function onContinueDrag(e) {
+ e.preventDefault();
+
+ // console.log("mousemove at " + e.pageX + " / " + e.pageY + ", down=" +
+ // mouseIsDown);
+ if (mouseIsDown) {
+ sendMouseUpdate(true, e);
+ }
+ }
+
+ function sendMouseUpdate(down, e) {
+ console.assert(deviceConnection, 'Can\'t send mouse update without device');
+ var x = e.offsetX;
+ var y = e.offsetY;
+
+ const videoWidth = deviceScreen.videoWidth;
+ const videoHeight = deviceScreen.videoHeight;
+ const elementWidth = deviceScreen.offsetWidth;
+ const elementHeight = deviceScreen.offsetHeight;
+
+ // vh*ew > eh*vw? then scale h instead of w
+ const scaleHeight = videoHeight * elementWidth > videoWidth * elementHeight;
+ var elementScaling = 0, videoScaling = 0;
+ if (scaleHeight) {
+ elementScaling = elementHeight;
+ videoScaling = videoHeight;
+ } else {
+ elementScaling = elementWidth;
+ videoScaling = videoWidth;
+ }
+
+ // Substract the offset produced by the difference in aspect ratio if any.
+ if (scaleHeight) {
+ x -= (elementWidth - elementScaling * videoWidth / videoScaling) / 2;
+ } else {
+ y -= (elementHeight - elementScaling * videoHeight / videoScaling) / 2;
+ }
+
+ // Convert to coordinates relative to the video
+ x = videoScaling * x / elementScaling;
+ y = videoScaling * y / elementScaling;
+
+ deviceConnection.sendMousePosition(
+ {x: Math.trunc(x), y: Math.trunc(y), down});
+ }
+
+ 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, use_tcp) {
+ 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, use_tcp);
+}
+
+function ShowNewDeviceList(device_ids) {
+ let ul = document.getElementById('device_list');
+ ul.innerHTML = "";
+ for (const dev_id of device_ids) {
+ ul.innerHTML += ('<li class="device_entry" title="Connect to ' + dev_id
+ + '">' + dev_id + '<button onclick="ConnectDeviceCb(\''
+ + dev_id + '\', false)">Connect</button><button '
+ + 'onclick="ConnectDeviceCb(\'' + dev_id + '\', true)"'
+ + ' title="Useful when a proxy or firewall forbid UDP '
+ + 'connections">Connect over TCP only</button></li>');
+ }
+}
+
+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);
+ };
+}
+
+// Get any devices that are already connected
+UpdateDeviceList();
+// Update the list at the user's request
+document.getElementById('refresh_list').onclick = evt => UpdateDeviceList();
diff --git a/host/frontend/gcastv2/signaling_server/assets/js/cf_webrtc.js b/host/frontend/gcastv2/signaling_server/assets/js/cf_webrtc.js
new file mode 100644
index 0000000..d5c054d
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/assets/js/cf_webrtc.js
@@ -0,0 +1,431 @@
+// Javascript provides atob() and btoa() for base64 encoding and decoding, but
+// those don't work with binary data.
+class Base64 {
+ static base64Array = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ static encode(buffer) {
+ let data = new Uint8Array(buffer);
+ let size = data.length;
+ let ret = '';
+ let i = 0;
+ for (; i < size - size%3; i += 3) {
+ let x1 = data[i];
+ let x2 = data[i+1];
+ let x3 = data[i+2];
+
+ let accum = (x1 * 256 + x2 ) * 256 + x3;
+ ret += this.base64Array[(accum >> 18) % 64];
+ ret += this.base64Array[(accum >> 12) % 64];
+ ret += this.base64Array[(accum >> 6) % 64];
+ ret += this.base64Array[accum % 64];
+ }
+ switch (size % 3) {
+ case 1:
+ ret += this.base64Array[data[i] >> 2];
+ ret += this.base64Array[(data[i] % 4)*16];
+ ret += '==';
+ break;
+ case 2:
+ ret += this.base64Array[data[i] >> 2];
+ ret += this.base64Array[(data[i] % 4)*16 + (data[i+1] >> 4)];
+ ret += this.base64Array[(data[i] % 16) * 4];
+ ret += '=';
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+ static decode(str) {
+ if ((str.length % 4) != 0) {
+ throw "Invalid base 64";
+ }
+ let n = str.length;
+ let padding = 0;
+ if (n >= 1 && str[n-1] === '=') {
+ padding = 1;
+ if (n >= 2 && str[n-2] == '=') {
+ padding = 2;
+ }
+ }
+ let outLen = (3 * n / 4) - padding;
+ let out = new Uint8Array(outLen);
+
+ let j = 0;
+ let accum = 0;
+ for (let i = 0; i < n; i++) {
+ let value = this.base64Array.indexOf(str[i]);
+ if (str[i] === '=') {
+ if (i < n - padding) {
+ throw 'Invalid base 64';
+ }
+ value = 0;
+ } else if (value < 0) {
+ throw "Invalid base 64 char: " + str[i];
+ }
+ accum = accum * 64 + value;
+ if (((i+1)%4) == 0) {
+ out[j++] = accum >> 16;
+ if (j < outLen) {
+ out[j++] = (accum >> 8) % 256;
+ }
+ if (j < outLen) {
+ out[j++] = accum % 256;
+ }
+ accum = 0;
+ }
+ }
+
+ return out.buffer;
+ }
+}
+
+function createInputDataChannelPromise(pc) {
+ console.log("creating data channel");
+ let inputChannel = pc.createDataChannel('input-channel');
+ return new Promise((resolve, reject) => {
+ inputChannel.onopen = (event) => {
+ resolve(inputChannel);
+ };
+ inputChannel.onclose = () => {
+ console.log(
+ 'handleDataChannelStatusChange state=' + dataChannel.readyState);
+ };
+ inputChannel.onmessage = (msg) => {
+ console.log('handleDataChannelMessage data="' + msg.data + '"');
+ };
+ inputChannel.onerror = err => {
+ reject(err);
+ };
+ });
+}
+
+class DeviceConnection {
+ constructor(pc, control) {
+ this._pc = pc;
+ this._control = control;
+ this._inputChannelPr = createInputDataChannelPromise(pc);
+ this._streams = {};
+ this._streamPromiseResolvers = {};
+
+ pc.addEventListener('track', e => {
+ console.log('Got remote stream: ', e);
+ for (const stream of e.streams) {
+ this._streams[stream.id] = stream;
+ if (this._streamPromiseResolvers[stream.id]) {
+ for (let resolver of this._streamPromiseResolvers[stream.id]) {
+ resolver();
+ }
+ delete this._streamPromiseResolvers[stream.id];
+ }
+ }
+ });
+ }
+
+ set description(desc) {
+ this._description = desc;
+ }
+
+ get description() {
+ return this._description;
+ }
+
+ getStream(stream_id) {
+ return new Promise((resolve, reject) => {
+ if (this._streams[stream_id]) {
+ resolve(this._streams[stream_id]);
+ } else {
+ if (!this._streamPromiseResolvers[stream_id]) {
+ this._streamPromiseResolvers[stream_id] = [];
+ }
+ this._streamPromiseResolvers[stream_id].push(resolve);
+ }
+ });
+ }
+
+ _sendJsonInput(evt) {
+ this._inputChannelPr = this._inputChannelPr.then(inputChannel => {
+ inputChannel.send(JSON.stringify(evt));
+ return inputChannel;
+ });
+ }
+
+ sendMousePosition({x, y, down, display = 0}) {
+ this._sendJsonInput({
+ type: 'mouse',
+ down: down ? 1 : 0,
+ x,
+ y,
+ });
+ }
+
+ sendMultiTouch({id, x, y, initialDown, slot, display = 0}) {
+ this._sendJsonInput({
+ type: 'multi-touch',
+ id,
+ x,
+ y,
+ initialDown: initialDown ? 1 : 0,
+ slot,
+ });
+ }
+
+ sendKeyEvent(code, type) {
+ this._sendJsonInput({type: 'keyboard', keycode: code, event_type: type});
+ }
+
+ disconnect() {
+ this._pc.close();
+ }
+
+ // Sends binary data directly to the in-device adb daemon (skipping the host)
+ sendAdbMessage(msg) {
+ // TODO(b/148086548) send over data channel instead of websocket
+ this._control.sendAdbMessage(Base64.encode(msg));
+ }
+
+ // Provide a callback to receive data from the in-device adb daemon
+ onAdbMessage(cb) {
+ // TODO(b/148086548) send over data channel instead of websocket
+ this._control.onAdbMessage(msg => cb(Base64.decode(msg)));
+ }
+}
+
+
+class WebRTCControl {
+ constructor({
+ wsUrl = '',
+ disable_audio = false,
+ bundle_tracks = false,
+ use_tcp = true,
+ }) {
+ /*
+ * Private attributes:
+ *
+ * _options
+ *
+ * _wsPromise: promises the underlying websocket, should resolve when the
+ * socket passes to OPEN state, will be rejecte/replaced by a
+ * rejected promise if an error is detected on the socket.
+ *
+ * _onOffer
+ * _onIceCandidate
+ */
+
+ this._options = {
+ disable_audio,
+ bundle_tracks,
+ use_tcp,
+ };
+
+ this._promiseResolvers = {};
+
+ this._wsPromise = new Promise((resolve, reject) => {
+ let ws = new WebSocket(wsUrl);
+ ws.onopen = () => {
+ console.info(`Connected to ${wsUrl}`);
+ resolve(ws);
+ };
+ ws.onerror = evt => {
+ console.error('WebSocket error:', evt);
+ reject(evt);
+ // If the promise was already resolved the previous line has no effect
+ this._wsPromise = Promise.reject(new Error(evt));
+ };
+ ws.onmessage = e => {
+ let data = JSON.parse(e.data);
+ this._onWebsocketMessage(data);
+ };
+ });
+ }
+
+ _onWebsocketMessage(message) {
+ const type = message.message_type;
+ if (message.error) {
+ console.error(message.error);
+ return;
+ }
+ switch (type) {
+ case 'config':
+ this._infra_config = message;
+ break;
+ case 'device_info':
+ if (this._on_device_available) {
+ this._on_device_available(message.device_info);
+ delete this._on_device_available;
+ } else {
+ console.error('Received unsolicited device info');
+ }
+ break;
+ case 'device_msg':
+ this._onDeviceMessage(message.payload);
+ break;
+ default:
+ console.error('Unrecognized message type from server: ', type);
+ console.error(message);
+ }
+ }
+
+ _onDeviceMessage(message) {
+ let type = message.type;
+ switch(type) {
+ case 'offer':
+ if (this._onOffer) {
+ this._onOffer({type: 'offer', sdp: message.sdp});
+ } else {
+ console.error('Receive offer, but nothing is wating for it');
+ }
+ break;
+ case 'ice-candidate':
+ if (this._onIceCandidate) {
+ this._onIceCandidate(new RTCIceCandidate({
+ sdpMid: message.mid,
+ sdpMLineIndex: message.mLineIndex,
+ candidate: message.candidate}));
+ } else {
+ console.error('Received ice candidate but nothing is waiting for it');
+ }
+ break;
+ case 'adb-message':
+ if (this._onAdbMessage) {
+ this._onAdbMessage(message.payload);
+ }
+ break;
+ default:
+ console.error('Unrecognized message type from device: ', type);
+ }
+ }
+
+ async _wsSendJson(obj) {
+ let ws = await this._wsPromise;
+ return ws.send(JSON.stringify(obj));
+ }
+ async _sendToDevice(payload) {
+ this._wsSendJson({message_type: 'forward', payload});
+ }
+
+ onOffer(cb) {
+ this._onOffer = cb;
+ }
+
+ onIceCandidate(cb) {
+ this._onIceCandidate = cb;
+ }
+
+ async requestDevice(device_id) {
+ return new Promise((resolve, reject) => {
+ this._on_device_available = (deviceInfo) => resolve({
+ deviceInfo,
+ infraConfig: this._infra_config,
+ });
+ this._wsSendJson({
+ message_type: 'connect',
+ device_id,
+ });
+ });
+ }
+
+ ConnectDevice() {
+ console.log('ConnectDevice');
+ const is_chrome = navigator.userAgent.indexOf('Chrome') !== -1;
+ this._sendToDevice({type: 'request-offer', options: this._options, is_chrome: is_chrome ? 1 : 0});
+ }
+
+ /**
+ * Sends a remote description to the device.
+ */
+ async sendClientDescription(desc) {
+ console.log('sendClientDescription');
+ this._sendToDevice({type: 'answer', sdp: desc.sdp});
+ }
+
+ /**
+ * Sends an ICE candidate to the device
+ */
+ async sendIceCandidate(candidate) {
+ this._sendToDevice({type: 'ice-candidate', candidate});
+ }
+
+ sendAdbMessage(msg) {
+ this._sendToDevice({type: 'adb-message', payload: msg});
+ }
+
+ onAdbMessage(cb) {
+ this._onAdbMessage = cb;
+ }
+}
+
+function createPeerConnection(infra_config) {
+ let pc_config = {iceServers:[]};
+ for (const stun of infra_config.ice_servers) {
+ pc_config.iceServers.push({urls: 'stun:' + stun});
+ }
+ let pc = new RTCPeerConnection(pc_config);
+
+ pc.addEventListener('icecandidate', evt => {
+ console.log('Local ICE Candidate: ', evt.candidate);
+ });
+ pc.addEventListener('iceconnectionstatechange', evt => {
+ console.log(`ICE State Change: ${pc.iceConnectionState}`);
+ });
+ pc.addEventListener('connectionstatechange', evt =>
+ console.log(`WebRTC Connection State Change: ${pc.connectionState}`));
+ return pc;
+}
+
+export async function Connect(deviceId, options) {
+ let control = new WebRTCControl(options);
+ let requestRet = await control.requestDevice(deviceId);
+ let deviceInfo = requestRet.deviceInfo;
+ let infraConfig = requestRet.infraConfig;
+ console.log("Device available:");
+ console.log(deviceInfo);
+ let pc_config = {
+ iceServers: []
+ };
+ if (infraConfig.ice_servers && infraConfig.ice_servers.length > 0) {
+ for (const server of infraConfig.ice_servers) {
+ pc_config.iceServers.push(server);
+ }
+ }
+ let pc = createPeerConnection(infraConfig, control);
+ let deviceConnection = new DeviceConnection(pc, control);
+ deviceConnection.description = deviceInfo;
+ async function acceptOfferAndReplyAnswer(offer) {
+ try {
+ await pc.setRemoteDescription(offer);
+ let answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+ await control.sendClientDescription(answer);
+ } catch(e) {
+ console.error('Error establishing WebRTC connection: ', e)
+ throw e;
+ }
+ }
+ control.onOffer(desc => {
+ console.log('Offer: ', desc);
+ acceptOfferAndReplyAnswer(desc);
+ });
+ control.onIceCandidate(iceCandidate => {
+ console.log(`Remote ICE Candidate: `, iceCandidate);
+ pc.addIceCandidate(iceCandidate);
+ });
+
+ pc.addEventListener('icecandidate',
+ evt => {
+ if (evt.candidate)
+ control.sendIceCandidate(evt.candidate);
+ });
+ let connected_promise = new Promise((resolve, reject) => {
+ pc.addEventListener('connectionstatechange', evt => {
+ let state = pc.connectionState;
+ if (state == 'connected') {
+ resolve(deviceConnection);
+ } else if (state == 'failed') {
+ reject(evt);
+ }
+ });
+ });
+ control.ConnectDevice();
+
+ return connected_promise;
+}
diff --git a/host/frontend/gcastv2/webrtc/assets/js/logcat.js b/host/frontend/gcastv2/signaling_server/assets/js/logcat.js
similarity index 88%
rename from host/frontend/gcastv2/webrtc/assets/js/logcat.js
rename to host/frontend/gcastv2/signaling_server/assets/js/logcat.js
index 1753bb6..e1ee94f 100644
--- a/host/frontend/gcastv2/webrtc/assets/js/logcat.js
+++ b/host/frontend/gcastv2/signaling_server/assets/js/logcat.js
@@ -131,7 +131,8 @@
{
let payloadText = utf8Decoder.decode(array.slice(24));
- logcat.value += payloadText;
+ // Limit to 100 lines
+ logcat.value = (logcat.value + payloadText).split('\n').slice(-100).join('\n');
// Scroll to bottom
logcat.scrollTop = logcat.scrollHeight;
@@ -143,20 +144,15 @@
}
}
-function init_logcat() {
- const wsProtocol = (location.protocol == "http:") ? "ws:" : "wss:";
-
- adb_ws = new WebSocket(
- wsProtocol + "//" + location.host + "/control_adb");
-
- adb_ws.binaryType = "arraybuffer";
-
- adb_ws.onopen = function() {
- console.log("adb_ws: onopen");
-
- adbOpenConnection();
-
- logcat.style.display = "initial";
+function init_logcat(devConn) {
+ adb_ws = {
+ send: function(buffer) {
+ devConn.sendAdbMessage(buffer);
+ }
};
- adb_ws.onmessage = adbOnMessage;
+
+ logcat.style.display = "initial";
+ devConn.onAdbMessage(msg => adbOnMessage({data: msg}));
+
+ adbOpenConnection();
}
diff --git a/host/frontend/gcastv2/signaling_server/assets/style.css b/host/frontend/gcastv2/signaling_server/assets/style.css
new file mode 100644
index 0000000..dba8b03
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/assets/style.css
@@ -0,0 +1,64 @@
+body {
+ background-color:black
+}
+
+#device_connection {
+ visibility: hidden;
+}
+
+#device_selector {
+ color: whitesmoke;
+}
+#device_selector li.device_entry {
+ cursor: pointer;
+}
+
+.noscroll {
+ touch-action: none;
+}
+
+.one {
+ float: left;
+}
+
+#logcat {
+ display: none;
+ font-family: monospace;
+ padding: 10px;
+}
+
+button.selected {
+ background-color: #aaaaaa;
+ border-style: solid;
+ border-color: #aaaaaa;
+}
+
+
+.noscroll {
+ text-align: center;
+}
+
+section.noscroll div.one {
+ display: inline-block;
+ width: 100%;
+ height: 90%;
+}
+
+#deviceScreen {
+ max-width: 100%;
+ max-height: 100%;
+}
+
+#refresh_list {
+ cursor: pointer;
+}
+
+#device_list .device_entry button {
+ margin-left: 10px;
+}
+
+#logcat {
+ color: #aaaaaa;
+ background-color: black;
+ margin-top: 40px;
+}
diff --git a/host/frontend/gcastv2/webrtc/certs/create_certs.sh b/host/frontend/gcastv2/signaling_server/certs/create_certs.sh
similarity index 100%
rename from host/frontend/gcastv2/webrtc/certs/create_certs.sh
rename to host/frontend/gcastv2/signaling_server/certs/create_certs.sh
diff --git a/host/frontend/gcastv2/signaling_server/certs/server.crt b/host/frontend/gcastv2/signaling_server/certs/server.crt
new file mode 100644
index 0000000..0b9aa36
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/certs/server.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVzCCAj8CFBXBydw0e/7l31d9fzO7vrBxAw4yMA0GCSqGSIb3DQEBCwUAMGgx
+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==
+-----END CERTIFICATE-----
diff --git a/host/frontend/gcastv2/webrtc/certs/server.key b/host/frontend/gcastv2/signaling_server/certs/server.key
similarity index 100%
rename from host/frontend/gcastv2/webrtc/certs/server.key
rename to host/frontend/gcastv2/signaling_server/certs/server.key
diff --git a/host/frontend/gcastv2/signaling_server/certs/server.p12 b/host/frontend/gcastv2/signaling_server/certs/server.p12
new file mode 100644
index 0000000..87a94c5
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/certs/server.p12
Binary files differ
diff --git a/host/frontend/gcastv2/signaling_server/certs/trusted.pem b/host/frontend/gcastv2/signaling_server/certs/trusted.pem
new file mode 100644
index 0000000..8097b16
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/certs/trusted.pem
@@ -0,0 +1,70 @@
+Certificate:
+ 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
+ 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
+ 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
+ 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
+-----BEGIN CERTIFICATE-----
+MIIDVzCCAj8CFBXBydw0e/7l31d9fzO7vrBxAw4yMA0GCSqGSIb3DQEBCwUAMGgx
+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==
+-----END CERTIFICATE-----
diff --git a/host/frontend/gcastv2/signaling_server/client_handler.cpp b/host/frontend/gcastv2/signaling_server/client_handler.cpp
new file mode 100644
index 0000000..3643528
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/client_handler.cpp
@@ -0,0 +1,105 @@
+//
+// 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 "host/frontend/gcastv2/signaling_server/client_handler.h"
+
+#include <android-base/logging.h>
+
+#include "host/frontend/gcastv2/signaling_server/constants/signaling_constants.h"
+#include "host/frontend/gcastv2/signaling_server/device_handler.h"
+
+namespace cvd {
+
+ClientHandler::ClientHandler(DeviceRegistry* registry,
+ const ServerConfig& server_config)
+ : SignalHandler(registry, server_config),
+ device_handler_(),
+ client_id_(0) {}
+
+void ClientHandler::SendDeviceMessage(const Json::Value& device_message) {
+ Json::Value message;
+ message[webrtc_signaling::kTypeField] = webrtc_signaling::kDeviceMessageType;
+ message[webrtc_signaling::kPayloadField] = device_message;
+ Reply(message);
+}
+
+int ClientHandler::handleMessage(const std::string& type,
+ const Json::Value& message) {
+ if (type == webrtc_signaling::kConnectType) {
+ return handleConnectionRequest(message);
+ } else if (type == webrtc_signaling::kForwardType) {
+ return handleForward(message);
+ } else {
+ LogAndReplyError("Unknown message type: " + type);
+ return -1;
+ }
+}
+
+int ClientHandler::handleConnectionRequest(const Json::Value& message) {
+ if (client_id_ > 0) {
+ LOG(ERROR) << "Detected attempt to connect to multiple devices over same "
+ "websocket";
+ return -EINVAL;
+ }
+ if (!message.isMember(webrtc_signaling::kDeviceIdField) ||
+ !message[webrtc_signaling::kDeviceIdField].isString()) {
+ LogAndReplyError("Invalid connection request: Missing device id");
+ return -EINVAL;
+ }
+ auto device_id = message[webrtc_signaling::kDeviceIdField].asString();
+ // Always send the server config back, even if the requested device is not
+ // registered. Applications may put clients on hold until the device is ready
+ // to connect.
+ SendServerConfig();
+
+ auto device_handler = registry_->GetDevice(device_id);
+ if (!device_handler) {
+ LogAndReplyError("Connection failed: Device not found: '" + device_id +
+ "'");
+ return -1;
+ }
+
+ client_id_ = device_handler->RegisterClient(shared_from_this());
+ device_handler_ = device_handler;
+ Json::Value device_info_reply;
+ device_info_reply[webrtc_signaling::kTypeField] =
+ webrtc_signaling::kDeviceInfoType;
+ device_info_reply[webrtc_signaling::kDeviceInfoField] =
+ device_handler->device_info();
+ Reply(device_info_reply);
+ return 0;
+}
+
+int ClientHandler::handleForward(const Json::Value& message) {
+ if (client_id_ == 0) {
+ LogAndReplyError("Forward failed: No device asociated to client");
+ return 0;
+ }
+ if (!message.isMember(webrtc_signaling::kPayloadField)) {
+ LogAndReplyError("Forward failed: No payload present in message");
+ return 0;
+ }
+ auto device_handler = device_handler_.lock();
+ if (!device_handler) {
+ LogAndReplyError("Forward failed: Device disconnected");
+ // Disconnect this client since the device is gone
+ return -1;
+ }
+ device_handler->SendClientMessage(client_id_,
+ message[webrtc_signaling::kPayloadField]);
+ return 0;
+}
+
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/client_handler.h b/host/frontend/gcastv2/signaling_server/client_handler.h
new file mode 100644
index 0000000..afd3c7c
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/client_handler.h
@@ -0,0 +1,48 @@
+//
+// 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 <memory>
+#include <string>
+
+#include <json/json.h>
+
+#include "host/frontend/gcastv2/https/include/https/WebSocketHandler.h"
+#include "host/frontend/gcastv2/signaling_server/device_registry.h"
+#include "host/frontend/gcastv2/signaling_server/server_config.h"
+#include "host/frontend/gcastv2/signaling_server/signal_handler.h"
+
+namespace cvd {
+class DeviceHandler;
+class ClientHandler : public SignalHandler,
+ public std::enable_shared_from_this<ClientHandler> {
+ public:
+ ClientHandler(DeviceRegistry* registry, const ServerConfig& server_config);
+ void SendDeviceMessage(const Json::Value& message);
+ protected:
+ int handleMessage(const std::string& type,
+ const Json::Value& message) override;
+
+ private:
+ int handleConnectionRequest(const Json::Value& message);
+ int handleForward(const Json::Value& message);
+
+ std::weak_ptr<DeviceHandler> device_handler_;
+ // The device handler assigns this to each client to be able to differentiate
+ // them.
+ size_t client_id_;
+};
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/constants/signaling_constants.h b/host/frontend/gcastv2/signaling_server/constants/signaling_constants.h
new file mode 100644
index 0000000..b3b2870
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/constants/signaling_constants.h
@@ -0,0 +1,42 @@
+//
+// 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
+
+namespace cvd {
+namespace webrtc_signaling {
+
+constexpr auto kTypeField = "message_type";
+constexpr auto kDeviceInfoField = "device_info";
+constexpr auto kDeviceIdField = "device_id";
+constexpr auto kClientIdField = "client_id";
+constexpr auto kPayloadField = "payload";
+constexpr auto kServersField = "ice_servers";
+// These are defined in the IceServer dictionary
+constexpr auto kUrlsField = "urls";
+constexpr auto kUsernameField = "username";
+constexpr auto kCredentialField = "credential";
+constexpr auto kCredentialTypeField = "credentialType";
+
+constexpr auto kRegisterType = "register";
+constexpr auto kForwardType = "forward";
+constexpr auto kConfigType = "config";
+constexpr auto kConnectType = "connect";
+constexpr auto kDeviceInfoType = "device_info";
+constexpr auto kClientMessageType = "client_msg";
+constexpr auto kDeviceMessageType = "device_msg";
+
+} // namespace webrtc_signaling
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/device_handler.cpp b/host/frontend/gcastv2/signaling_server/device_handler.cpp
new file mode 100644
index 0000000..b48f6c9
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/device_handler.cpp
@@ -0,0 +1,115 @@
+//
+// 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 "host/frontend/gcastv2/signaling_server/device_handler.h"
+
+#include <android-base/logging.h>
+
+#include "host/frontend/gcastv2/signaling_server/client_handler.h"
+#include "host/frontend/gcastv2/signaling_server/constants/signaling_constants.h"
+
+namespace cvd {
+
+DeviceHandler::DeviceHandler(DeviceRegistry* registry,
+ const ServerConfig& server_config)
+ : SignalHandler(registry, server_config), device_info_(), clients_() {}
+
+DeviceHandler::~DeviceHandler() {
+ // Unregister the device when the websocket connection is closed
+ if (!device_id_.empty() && registry_) {
+ registry_->UnRegisterDevice(device_id_);
+ }
+}
+
+size_t DeviceHandler::RegisterClient(
+ std::shared_ptr<ClientHandler> client_handler) {
+ clients_.emplace_back(client_handler);
+ return clients_.size();
+}
+
+int DeviceHandler::handleMessage(const std::string& type,
+ const Json::Value& message) {
+ if (type == webrtc_signaling::kRegisterType) {
+ return HandleRegistrationRequest(message);
+ } else if (type == webrtc_signaling::kForwardType) {
+ return HandleForward(message);
+ } else {
+ LogAndReplyError("Unknown message type: " + type);
+ }
+
+ return 0;
+}
+
+int DeviceHandler::HandleRegistrationRequest(const Json::Value& message) {
+ if (!device_id_.empty()) {
+ LogAndReplyError("Device already registered: " + device_id_);
+ return -EINVAL;
+ }
+ if (!message.isMember(webrtc_signaling::kDeviceIdField) ||
+ !message[webrtc_signaling::kDeviceIdField].isString() ||
+ message[webrtc_signaling::kDeviceIdField].asString().empty()) {
+ LogAndReplyError("Missing device id in registration request");
+ return -EINVAL;
+ }
+ device_id_ = message[webrtc_signaling::kDeviceIdField].asString();
+ if (message.isMember(webrtc_signaling::kDeviceInfoField)) {
+ device_info_ = message[webrtc_signaling::kDeviceInfoField];
+ }
+ if (!registry_->RegisterDevice(device_id_, weak_from_this())) {
+ LOG(ERROR) << "Device registration failed";
+ return -1;
+ }
+
+ SendServerConfig();
+
+ return 0;
+}
+
+int DeviceHandler::HandleForward(const Json::Value& message) {
+ if (!message.isMember(webrtc_signaling::kClientIdField) || !message[webrtc_signaling::kClientIdField].isInt()) {
+ LogAndReplyError("Forward failed: Missing or invalid client id");
+ return 0;
+ }
+ size_t client_id = message[webrtc_signaling::kClientIdField].asInt();
+ if (!message.isMember(webrtc_signaling::kPayloadField)) {
+ LogAndReplyError("Forward failed: Missing payload");
+ return 0;
+ }
+ if (client_id > clients_.size()) {
+ LogAndReplyError("Forward failed: Unknown client " +
+ std::to_string(client_id));
+ return 0;
+ }
+ auto client_index = client_id - 1;
+ auto client_handler = clients_[client_index].lock();
+ if (!client_handler) {
+ LogAndReplyError("Forward failed: Client " + std::to_string(client_id) +
+ " disconnected");
+ return 0;
+ }
+ client_handler->SendDeviceMessage(message[webrtc_signaling::kPayloadField]);
+ return 0;
+}
+
+void DeviceHandler::SendClientMessage(size_t client_id,
+ const Json::Value& client_message) {
+ Json::Value msg;
+ msg[webrtc_signaling::kTypeField] = webrtc_signaling::kClientMessageType;
+ msg[webrtc_signaling::kClientIdField] = static_cast<Json::UInt>(client_id);
+ msg[webrtc_signaling::kPayloadField] = client_message;
+ Reply(msg);
+}
+
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/device_handler.h b/host/frontend/gcastv2/signaling_server/device_handler.h
new file mode 100644
index 0000000..b94a38a
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/device_handler.h
@@ -0,0 +1,56 @@
+//
+// 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 <memory>
+#include <string>
+#include <vector>
+
+#include <json/json.h>
+
+#include "host/frontend/gcastv2/https/include/https/WebSocketHandler.h"
+#include "host/frontend/gcastv2/signaling_server/device_registry.h"
+#include "host/frontend/gcastv2/signaling_server/server_config.h"
+#include "host/frontend/gcastv2/signaling_server/signal_handler.h"
+
+namespace cvd {
+
+class ClientHandler;
+
+class DeviceHandler
+ : public SignalHandler,
+ public std::enable_shared_from_this<DeviceHandler> {
+ public:
+ DeviceHandler(DeviceRegistry* registry, const ServerConfig& server_config);
+ ~DeviceHandler() override;
+
+ Json::Value device_info() const { return device_info_; }
+
+ size_t RegisterClient(std::shared_ptr<ClientHandler> client_handler);
+ void SendClientMessage(size_t client_id, const Json::Value& message);
+ protected:
+ int handleMessage(const std::string& type,
+ const Json::Value& message) override;
+
+ private:
+ int HandleRegistrationRequest(const Json::Value& message);
+ int HandleForward(const Json::Value& message);
+
+ std::string device_id_;
+ Json::Value device_info_;
+ std::vector<std::weak_ptr<ClientHandler>> clients_;
+};
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/device_list_handler.cpp b/host/frontend/gcastv2/signaling_server/device_list_handler.cpp
new file mode 100644
index 0000000..ac946ae
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/device_list_handler.cpp
@@ -0,0 +1,38 @@
+//
+// 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 "host/frontend/gcastv2/signaling_server/device_list_handler.h"
+
+namespace cvd {
+
+DeviceListHandler::DeviceListHandler(const DeviceRegistry& registry)
+ : registry_(registry) {}
+
+int DeviceListHandler::handleMessage(uint8_t /*header_byte*/,
+ const uint8_t* /*msg*/,
+ size_t /*len*/) {
+ // ignore the message, just send the reply
+ Json::Value reply(Json::ValueType::arrayValue);
+
+ for (const auto& id : registry_.ListDeviceIds()) {
+ reply.append(id);
+ }
+ Json::FastWriter json_writer;
+ auto replyAsString = json_writer.write(reply);
+ sendMessage(replyAsString.c_str(), replyAsString.size());
+ return -1; // disconnect
+}
+
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/device_list_handler.h b/host/frontend/gcastv2/signaling_server/device_list_handler.h
new file mode 100644
index 0000000..0d099f1
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/device_list_handler.h
@@ -0,0 +1,38 @@
+//
+// 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 <memory>
+#include <string>
+
+#include <json/json.h>
+
+#include "host/frontend/gcastv2/https/include/https/WebSocketHandler.h"
+#include "host/frontend/gcastv2/signaling_server/device_registry.h"
+
+namespace cvd {
+
+class DeviceListHandler : public WebSocketHandler {
+ public:
+ DeviceListHandler(const DeviceRegistry& registry);
+
+ protected:
+ int handleMessage(uint8_t, const uint8_t*, size_t) override;
+
+ private:
+ const DeviceRegistry& registry_;
+};
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/device_registry.cpp b/host/frontend/gcastv2/signaling_server/device_registry.cpp
new file mode 100644
index 0000000..f5dc362
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/device_registry.cpp
@@ -0,0 +1,71 @@
+//
+// 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 "host/frontend/gcastv2/signaling_server/device_registry.h"
+
+#include <android-base/logging.h>
+
+#include "host/frontend/gcastv2/signaling_server/device_handler.h"
+
+namespace cvd {
+
+bool DeviceRegistry::RegisterDevice(
+ const std::string& device_id,
+ std::weak_ptr<DeviceHandler> device_handler) {
+ if (devices_.count(device_id) > 0) {
+ LOG(ERROR) << "Device '" << device_id << "' is already registered";
+ return false;
+ }
+
+ devices_.try_emplace(device_id, device_handler);
+ LOG(INFO) << "Registered device: '" << device_id << "'";
+ return true;
+}
+
+void DeviceRegistry::UnRegisterDevice(const std::string& device_id) {
+ auto record = devices_.find(device_id);
+ if (record == devices_.end()) {
+ LOG(WARNING) << "Requested to unregister an unkwnown device: '" << device_id
+ << "'";
+ return;
+ }
+ devices_.erase(record);
+ LOG(INFO) << "Unregistered device: '" << device_id << "'";
+}
+
+std::shared_ptr<DeviceHandler> DeviceRegistry::GetDevice(
+ const std::string& device_id) {
+ if (devices_.count(device_id) == 0) {
+ LOG(INFO) << "Requested device (" << device_id << ") is not registered";
+ return nullptr;
+ }
+ auto device_handler = devices_[device_id].lock();
+ if (!device_handler) {
+ LOG(WARNING) << "Destroyed device handler detected for device '"
+ << device_id << "'";
+ UnRegisterDevice(device_id);
+ }
+ return device_handler;
+}
+
+std::vector<std::string> DeviceRegistry::ListDeviceIds() const {
+ std::vector<std::string> ret;
+ for (const auto& entry: devices_) {
+ ret.push_back(entry.first);
+ }
+ return ret;
+}
+
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/device_registry.h b/host/frontend/gcastv2/signaling_server/device_registry.h
new file mode 100644
index 0000000..0d2a46b
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/device_registry.h
@@ -0,0 +1,45 @@
+//
+// 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 <cinttypes>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <json/json.h>
+
+namespace cvd {
+
+class DeviceHandler;
+
+class DeviceRegistry {
+ public:
+ bool RegisterDevice(const std::string& device_id,
+ std::weak_ptr<DeviceHandler> device_handler);
+ void UnRegisterDevice(const std::string& device_id);
+
+ std::shared_ptr<DeviceHandler> GetDevice(const std::string& device_id);
+
+ std::vector<std::string> ListDeviceIds() const;
+
+ private:
+ std::map<std::string, std::weak_ptr<DeviceHandler>> devices_;
+};
+
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/server.cpp b/host/frontend/gcastv2/signaling_server/server.cpp
new file mode 100644
index 0000000..8fdf4c1
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/server.cpp
@@ -0,0 +1,122 @@
+//
+// 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/logging.h>
+#include <gflags/gflags.h>
+#include <netdb.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+
+#include "host/frontend/gcastv2/https/include/https/HTTPServer.h"
+#include "host/frontend/gcastv2/https/include/https/PlainSocket.h"
+#include "host/frontend/gcastv2/https/include/https/RunLoop.h"
+#include "host/frontend/gcastv2/https/include/https/SSLSocket.h"
+#include "host/frontend/gcastv2/https/include/https/SafeCallbackable.h"
+#include "host/frontend/gcastv2/https/include/https/Support.h"
+#include "host/frontend/gcastv2/webrtc/Utils.h"
+#include "host/frontend/gcastv2/webrtc/include/webrtc/STUNClient.h"
+#include "host/frontend/gcastv2/webrtc/include/webrtc/STUNMessage.h"
+
+#include "host/frontend/gcastv2/signaling_server/client_handler.h"
+#include "host/frontend/gcastv2/signaling_server/device_handler.h"
+#include "host/frontend/gcastv2/signaling_server/device_list_handler.h"
+#include "host/frontend/gcastv2/signaling_server/device_registry.h"
+#include "host/frontend/gcastv2/signaling_server/server_config.h"
+
+DEFINE_int32(http_server_port, 8443, "The port for the http server.");
+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(stun_server, "stun.l.google.com:19302",
+ "host:port of STUN server to use for public address resolution");
+
+namespace {
+
+void InitSSL() {
+ SSL_library_init();
+ SSL_load_error_strings();
+}
+
+void ServeStaticFiles(std::shared_ptr<HTTPServer> httpd) {
+ const std::string index_html = FLAGS_assets_dir + "/index.html";
+ const std::string logcat_js = FLAGS_assets_dir + "/js/logcat.js";
+ const std::string app_js = FLAGS_assets_dir + "/js/app.js";
+ const std::string viewpane_js = FLAGS_assets_dir + "/js/viewpane.js";
+ const std::string cf_webrtc_js = FLAGS_assets_dir + "/js/cf_webrtc.js";
+ const std::string style_css = FLAGS_assets_dir + "/style.css";
+
+ httpd->addStaticFile("/index.html", index_html.c_str());
+ httpd->addStaticFile("/js/logcat.js", logcat_js.c_str());
+ httpd->addStaticFile("/js/app.js", app_js.c_str());
+ httpd->addStaticFile("/js/viewpane.js", viewpane_js.c_str());
+ httpd->addStaticFile("/js/cf_webrtc.js", cf_webrtc_js.c_str());
+ httpd->addStaticFile("/style.css", style_css.c_str());
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+
+ InitSSL();
+
+ auto run_loop = RunLoop::main();
+
+ auto port = FLAGS_http_server_port;
+ /******************************************************************************
+ * WARNING!: The device registry doesn't need synchronization because it runs *
+ * in this run_loop. If a different run_loop or server implementation is used *
+ * synchronization all over needs to be revised. *
+ *****************************************************************************/
+
+ auto httpd = std::make_shared<HTTPServer>(
+ run_loop, "0.0.0.0", port,
+ FLAGS_use_secure_http ? ServerSocket::TransportType::TLS
+ : ServerSocket::TransportType::TCP,
+ FLAGS_certs_dir + "/server.crt", FLAGS_certs_dir + "/server.key");
+
+ ServeStaticFiles(httpd);
+
+ cvd::ServerConfig server_config({FLAGS_stun_server});
+ cvd::DeviceRegistry device_registry;
+
+ httpd->addWebSocketHandlerFactory(
+ "/register_device", [&device_registry, &server_config] {
+ return std::make_pair(0, std::make_shared<cvd::DeviceHandler>(
+ &device_registry, server_config));
+ });
+
+ httpd->addWebSocketHandlerFactory(
+ "/connect_client", [&device_registry, &server_config] {
+ return std::make_pair(0, std::make_shared<cvd::ClientHandler>(
+ &device_registry, server_config));
+ });
+
+ // This is non-standard utility endpoint, it's the simplest way for clients to
+ // obtain the ids of registered devices.
+ httpd->addWebSocketHandlerFactory("/list_devices", [&device_registry] {
+ return std::make_pair(
+ 0, std::make_shared<cvd::DeviceListHandler>(device_registry));
+ });
+
+ httpd->run();
+ run_loop->run();
+
+ return 0;
+}
diff --git a/host/frontend/gcastv2/signaling_server/server_config.cpp b/host/frontend/gcastv2/signaling_server/server_config.cpp
new file mode 100644
index 0000000..6fa880a
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/server_config.cpp
@@ -0,0 +1,43 @@
+//
+// 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 "host/frontend/gcastv2/signaling_server/server_config.h"
+
+#include <android-base/strings.h>
+
+using android::base::StartsWith;
+
+namespace cvd {
+
+namespace {
+ constexpr auto kStunPrefix = "stun:";
+}
+
+ServerConfig::ServerConfig(const std::vector<std::string>& stuns)
+ : stun_servers_(stuns) {}
+
+Json::Value ServerConfig::ToJson() const {
+ Json::Value ice_servers(Json::ValueType::arrayValue);
+ for (const auto& str : stun_servers_) {
+ Json::Value server;
+ server["urls"] = StartsWith(str, kStunPrefix)? str: kStunPrefix + str;
+ ice_servers.append(server);
+ }
+ Json::Value server_config;
+ server_config["ice_servers"] = ice_servers;
+ return server_config;
+}
+
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/server_config.h b/host/frontend/gcastv2/signaling_server/server_config.h
new file mode 100644
index 0000000..d4a42a5
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/server_config.h
@@ -0,0 +1,33 @@
+//
+// 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 <string>
+#include <vector>
+
+#include <json/json.h>
+
+namespace cvd {
+class ServerConfig {
+ public:
+ ServerConfig(const std::vector<std::string>& stuns);
+
+ Json::Value ToJson() const;
+
+ private:
+ std::vector<std::string> stun_servers_;
+};
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/signal_handler.cpp b/host/frontend/gcastv2/signaling_server/signal_handler.cpp
new file mode 100644
index 0000000..1df26cc
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/signal_handler.cpp
@@ -0,0 +1,79 @@
+//
+// 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 "host/frontend/gcastv2/signaling_server/signal_handler.h"
+
+#include <android-base/logging.h>
+#include <json/json.h>
+
+#include "host/frontend/gcastv2/signaling_server/constants/signaling_constants.h"
+
+namespace cvd {
+
+SignalHandler::SignalHandler(DeviceRegistry* registry,
+ const ServerConfig& server_config)
+ : registry_(registry), server_config_(server_config) {}
+
+bool SignalHandler::IsBinaryMessage(uint8_t header_byte) {
+ // https://tools.ietf.org/html/rfc6455#section-5.2
+ return (header_byte & 0x0f) == 0x02;
+}
+
+int SignalHandler::handleMessage(uint8_t header_byte, const uint8_t* msg,
+ size_t len) {
+ if (IsBinaryMessage(header_byte)) {
+ LOG(ERROR) << "Received a binary message";
+ return -EINVAL;
+ }
+ Json::Value json_message;
+ Json::Reader json_reader;
+ auto str = reinterpret_cast<const char*>(msg);
+ if (!json_reader.parse(str, str + len, json_message)) {
+ LOG(ERROR) << "Received Invalid JSON";
+ // Rate limiting would be a good idea here
+ return -EINVAL;
+ }
+ if (!json_message.isMember(webrtc_signaling::kTypeField) ||
+ !json_message[webrtc_signaling::kTypeField].isString()) {
+ LogAndReplyError("Invalid message format: '" + std::string(msg, msg + len) +
+ "'");
+ // Rate limiting would be a good idea here
+ return -EINVAL;
+ }
+
+ auto type = json_message[webrtc_signaling::kTypeField].asString();
+ return handleMessage(type, json_message);
+}
+
+void SignalHandler::SendServerConfig() {
+ // Call every time to allow config changes?
+ auto reply = server_config_.ToJson();
+ reply[webrtc_signaling::kTypeField] = webrtc_signaling::kConfigType;
+ Reply(reply);
+}
+
+void SignalHandler::LogAndReplyError(const std::string& error_message) {
+ LOG(ERROR) << error_message;
+ auto reply_str = "{\"error\":\"" + error_message + "\"}";
+ sendMessage(reply_str.c_str(), reply_str.size());
+}
+
+void SignalHandler::Reply(const Json::Value& json) {
+ Json::FastWriter json_writer;
+ auto replyAsString = json_writer.write(json);
+ sendMessage(replyAsString.c_str(), replyAsString.size());
+}
+
+} // namespace cvd
diff --git a/host/frontend/gcastv2/signaling_server/signal_handler.h b/host/frontend/gcastv2/signaling_server/signal_handler.h
new file mode 100644
index 0000000..c0bf96f
--- /dev/null
+++ b/host/frontend/gcastv2/signaling_server/signal_handler.h
@@ -0,0 +1,47 @@
+//
+// 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 <memory>
+#include <string>
+
+#include <json/json.h>
+
+#include "host/frontend/gcastv2/https/include/https/WebSocketHandler.h"
+#include "host/frontend/gcastv2/signaling_server/device_registry.h"
+#include "host/frontend/gcastv2/signaling_server/server_config.h"
+
+namespace cvd {
+
+class SignalHandler : public WebSocketHandler {
+ protected:
+ SignalHandler(DeviceRegistry* registry, const ServerConfig& server_config);
+
+ bool IsBinaryMessage(uint8_t header_byte);
+
+ int handleMessage(uint8_t header_byte, const uint8_t* msg,
+ size_t len) override;
+ virtual int handleMessage(const std::string& message_type,
+ const Json::Value& message) = 0;
+ void SendServerConfig();
+
+ void LogAndReplyError(const std::string& message);
+ void Reply(const Json::Value& json);
+
+ DeviceRegistry* registry_;
+ const ServerConfig& server_config_;
+};
+} // namespace cvd
diff --git a/host/frontend/gcastv2/webrtc/AdbWebSocketHandler.cpp b/host/frontend/gcastv2/webrtc/AdbHandler.cpp
similarity index 68%
rename from host/frontend/gcastv2/webrtc/AdbWebSocketHandler.cpp
rename to host/frontend/gcastv2/webrtc/AdbHandler.cpp
index e663d27..9d4c5c8 100644
--- a/host/frontend/gcastv2/webrtc/AdbWebSocketHandler.cpp
+++ b/host/frontend/gcastv2/webrtc/AdbHandler.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <webrtc/AdbWebSocketHandler.h>
+#include <webrtc/AdbHandler.h>
#include "Utils.h"
@@ -27,26 +27,26 @@
using namespace android;
-struct AdbWebSocketHandler::AdbConnection : public BaseConnection {
+struct AdbHandler::AdbConnection : public BaseConnection {
explicit AdbConnection(
- AdbWebSocketHandler *parent,
+ AdbHandler *parent,
std::shared_ptr<RunLoop> runLoop,
int sock);
void send(const void *_data, size_t size);
protected:
- ssize_t processClientRequest(const void *data, size_t size) override;
+ long processClientRequest(const void *data, size_t size) override;
void onDisconnect(int err) override;
private:
- AdbWebSocketHandler *mParent;
+ AdbHandler *mParent;
};
////////////////////////////////////////////////////////////////////////////////
-AdbWebSocketHandler::AdbConnection::AdbConnection(
- AdbWebSocketHandler *parent,
+AdbHandler::AdbConnection::AdbConnection(
+ AdbHandler *parent,
std::shared_ptr<RunLoop> runLoop,
int sock)
: BaseConnection(runLoop, sock),
@@ -99,7 +99,7 @@
return 0;
}
-ssize_t AdbWebSocketHandler::AdbConnection::processClientRequest(
+long AdbHandler::AdbConnection::processClientRequest(
const void *_data, size_t size) {
auto data = static_cast<const uint8_t *>(_data);
@@ -115,32 +115,29 @@
return err;
}
- mParent->sendMessage(
- data, payloadLength + 24, WebSocketHandler::SendMode::binary);
-
+ mParent->send_to_client_(data, payloadLength + 24);
return payloadLength + 24;
}
-void AdbWebSocketHandler::AdbConnection::onDisconnect(int err) {
+void AdbHandler::AdbConnection::onDisconnect(int err) {
LOG(INFO) << "AdbConnection::onDisconnect(err=" << err << ")";
- mParent->sendMessage(
- nullptr /* data */,
- 0 /* size */,
- WebSocketHandler::SendMode::closeConnection);
+ mParent->send_to_client_(nullptr /* data */, 0 /* size */);
}
-void AdbWebSocketHandler::AdbConnection::send(const void *_data, size_t size) {
+void AdbHandler::AdbConnection::send(const void *_data, size_t size) {
BaseConnection::send(_data, size);
}
////////////////////////////////////////////////////////////////////////////////
-AdbWebSocketHandler::AdbWebSocketHandler(
+AdbHandler::AdbHandler(
std::shared_ptr<RunLoop> runLoop,
- const std::string &adb_host_and_port)
+ const std::string &adb_host_and_port,
+ std::function<void(const uint8_t*, size_t)> send_to_client)
: mRunLoop(runLoop),
- mSocket(-1) {
+ mSocket(-1),
+ send_to_client_(send_to_client) {
LOG(INFO) << "Connecting to " << adb_host_and_port;
auto err = setupSocket(adb_host_and_port);
@@ -149,18 +146,18 @@
mAdbConnection = std::make_shared<AdbConnection>(this, mRunLoop, mSocket);
}
-AdbWebSocketHandler::~AdbWebSocketHandler() {
+AdbHandler::~AdbHandler() {
if (mSocket >= 0) {
close(mSocket);
mSocket = -1;
}
}
-void AdbWebSocketHandler::run() {
+void AdbHandler::run() {
mAdbConnection->run();
}
-int AdbWebSocketHandler::setupSocket(const std::string &adb_host_and_port) {
+int AdbHandler::setupSocket(const std::string &adb_host_and_port) {
auto colonPos = adb_host_and_port.find(':');
if (colonPos == std::string::npos) {
return -EINVAL;
@@ -213,47 +210,17 @@
return err;
}
-int AdbWebSocketHandler::handleMessage(
- uint8_t headerByte, const uint8_t *msg, size_t len) {
- LOG(VERBOSE)
- << "headerByte = "
- << StringPrintf("0x%02x", (unsigned)headerByte);
-
+void AdbHandler::handleMessage(const uint8_t *msg, size_t len) {
LOG(VERBOSE) << hexdump(msg, len);
- if (!(headerByte & 0x80)) {
- // I only want to receive whole messages here, not fragments.
- return -EINVAL;
+ size_t payloadLength;
+ int err = verifyAdbHeader(msg, len, &payloadLength);
+
+ if (err || len != 24 + payloadLength) {
+ LOG(ERROR) << "Not a valid adb message.";
+ return;
}
- auto opcode = headerByte & 0x1f;
- switch (opcode) {
- case 0x8:
- {
- // closeConnection.
- break;
- }
-
- case 0x2:
- {
- // binary
-
- size_t payloadLength;
- int err = verifyAdbHeader(msg, len, &payloadLength);
-
- if (err || len != 24 + payloadLength) {
- LOG(ERROR) << "websocket message is not a valid adb message.";
- return -EINVAL;
- }
-
- mAdbConnection->send(msg, len);
- break;
- }
-
- default:
- return -EINVAL;
- }
-
- return 0;
+ mAdbConnection->send(msg, len);
}
diff --git a/host/frontend/gcastv2/webrtc/Android.bp b/host/frontend/gcastv2/webrtc/Android.bp
index f662688..d406446 100644
--- a/host/frontend/gcastv2/webrtc/Android.bp
+++ b/host/frontend/gcastv2/webrtc/Android.bp
@@ -16,11 +16,11 @@
cc_library_static {
name: "libwebrtc",
srcs: [
- "AdbWebSocketHandler.cpp",
+ "AdbHandler.cpp",
"DTLS.cpp",
"G711Packetizer.cpp",
"Keyboard.cpp",
- "MyWebSocketHandler.cpp",
+ "client_handler.cpp",
"OpusPacketizer.cpp",
"Packetizer.cpp",
"RTPSender.cpp",
@@ -34,6 +34,11 @@
"STUNMessage.cpp",
"Utils.cpp",
"VP8Packetizer.cpp",
+ "ws_connection.cpp",
+ "sig_server_handler.cpp",
+ ],
+ header_libs: [
+ "webrtc_signaling_headers",
],
static_libs: [
"libhttps",
@@ -48,6 +53,9 @@
"libffi",
"libwayland_server",
"libwayland_extension_server_protocols",
+ "libwebsockets",
+ "libcap",
+ "libcuttlefish_utils",
],
shared_libs: [
"libssl",
@@ -88,73 +96,9 @@
"libffi",
"libwayland_server",
"libwayland_extension_server_protocols",
+ "libwebsockets",
+ "libcap",
],
cpp_std: "experimental",
defaults: ["cuttlefish_host_only"],
}
-
-// TODO(jemoreira): Ideally these files should be in $HOST_OUT/webrtc but I
-// couldn't find a module type that would produce that, prebuilt_usr_share_host
-// is the next best thing for now.
-prebuilt_usr_share_host {
- name: "webrtc_index.html",
- src: "assets/index.html",
- filename: "index.html",
- sub_dir: "webrtc/assets",
-}
-
-prebuilt_usr_share_host {
- name: "webrtc_style.css",
- src: "assets/style.css",
- filename: "style.css",
- sub_dir: "webrtc/assets",
-}
-
-prebuilt_usr_share_host {
- name: "webrtc_logcat.js",
- src: "assets/js/logcat.js",
- filename: "logcat.js",
- sub_dir: "webrtc/assets/js",
-}
-
-prebuilt_usr_share_host {
- name: "webrtc_cf.js",
- src: "assets/js/cf_webrtc.js",
- filename: "cf_webrtc.js",
- sub_dir: "webrtc/assets/js",
-}
-
-prebuilt_usr_share_host {
- name: "webrtc_app.js",
- src: "assets/js/app.js",
- filename: "app.js",
- sub_dir: "webrtc/assets/js",
-}
-
-prebuilt_usr_share_host {
- name: "webrtc_server.crt",
- src: "certs/server.crt",
- filename: "server.crt",
- sub_dir: "webrtc/certs",
-}
-
-prebuilt_usr_share_host {
- name: "webrtc_server.key",
- src: "certs/server.key",
- filename: "server.key",
- sub_dir: "webrtc/certs",
-}
-
-prebuilt_usr_share_host {
- name: "webrtc_server.p12",
- src: "certs/server.p12",
- filename: "server.p12",
- sub_dir: "webrtc/certs",
-}
-
-prebuilt_usr_share_host {
- name: "webrtc_trusted.pem",
- src: "certs/trusted.pem",
- filename: "trusted.pem",
- sub_dir: "webrtc/certs",
-}
diff --git a/host/frontend/gcastv2/webrtc/RTPSocketHandler.cpp b/host/frontend/gcastv2/webrtc/RTPSocketHandler.cpp
index a8fff04..835e062 100644
--- a/host/frontend/gcastv2/webrtc/RTPSocketHandler.cpp
+++ b/host/frontend/gcastv2/webrtc/RTPSocketHandler.cpp
@@ -17,7 +17,6 @@
#include <webrtc/RTPSocketHandler.h>
#include <webrtc/Keyboard.h>
-#include <webrtc/MyWebSocketHandler.h>
#include <webrtc/STUNMessage.h>
#include <Utils.h>
@@ -270,7 +269,7 @@
}
std::string RTPSocketHandler::getLocalIPString() const {
- return FLAGS_public_ip;
+ return mServerState->public_ip();
}
void RTPSocketHandler::run() {
@@ -280,6 +279,23 @@
} else {
mSocket->postRecv(makeSafeCallback(this, &RTPSocketHandler::onReceive));
}
+ ScheduleTimeOutCheck();
+}
+
+void RTPSocketHandler::ScheduleTimeOutCheck() {
+ // RFC 3550 describes the timeout calculation, which for two participants
+ // boils down to M*5s, with M being a constant usually set to 5.
+ mRunLoop->postWithDelay(std::chrono::seconds(25),
+ makeSafeCallback<RTPSocketHandler>(
+ this,
+ [](RTPSocketHandler *me) {
+ bool timed_out = me->CheckParticipantTimeOut();
+ if (timed_out) {
+ me->on_participant_disconnected_();
+ } else {
+ me->ScheduleTimeOutCheck();
+ }
+ }));
}
void RTPSocketHandler::onTCPConnect() {
@@ -379,17 +395,18 @@
mSocket->postRecv(makeSafeCallback(this, &RTPSocketHandler::onReceive));
}
+bool RTPSocketHandler::CheckParticipantTimeOut() {
+ bool previous_value = packet_received_since_last_check_;
+ packet_received_since_last_check_ = false;
+ return !previous_value;
+}
+
void RTPSocketHandler::onPacketReceived(
const sockaddr_storage &addr,
socklen_t addrLen,
uint8_t *data,
size_t n) {
-#if 0
- std::cout << "========================================" << std::endl;
-
- hexdump(data, n);
-#endif
-
+ packet_received_since_last_check_ = true;
STUNMessage msg(data, n);
if (!msg.isValid()) {
if (mDTLSConnected) {
@@ -732,6 +749,13 @@
continue;
}
+ if (errno == EPIPE) {
+ LOG(ERROR) << "Lost connection to peer: " << strerror(EPIPE);
+ offset = size;
+ disconnected = true;
+ break;
+ }
+
LOG(FATAL) << "Error sending: "<<strerror(errno);
} else if (n == 0) {
offset = size;
@@ -743,7 +767,12 @@
}
buf->erase(buf->begin(), buf->begin() + offset);
- if ((!mTcpOutBufferQueue.empty() || !buf->empty()) && !disconnected) {
+ if (disconnected) {
+ on_participant_disconnected_();
+ return;
+ }
+
+ if (!mTcpOutBufferQueue.empty() || !buf->empty()) {
mSendPending = true;
mSocket->postSend(
diff --git a/host/frontend/gcastv2/webrtc/ServerState.cpp b/host/frontend/gcastv2/webrtc/ServerState.cpp
index 1e8b1ca..144c4d6 100644
--- a/host/frontend/gcastv2/webrtc/ServerState.cpp
+++ b/host/frontend/gcastv2/webrtc/ServerState.cpp
@@ -102,7 +102,8 @@
}
std::shared_ptr<Packetizer> ServerState::getVideoPacketizer() {
- auto packetizer = mVideoPacketizer.lock();
+ std::lock_guard<std::mutex> autoLock(mPacketizerLock);
+ std::shared_ptr<Packetizer> packetizer = mVideoPacketizer;
if (!packetizer) {
switch (mVideoFormat) {
case VideoFormat::VP8:
@@ -125,7 +126,8 @@
}
std::shared_ptr<Packetizer> ServerState::getAudioPacketizer() {
- auto packetizer = mAudioPacketizer.lock();
+ std::lock_guard<std::mutex> autoLock(mPacketizerLock);
+ std::shared_ptr packetizer = mAudioPacketizer;
if (!packetizer) {
packetizer = std::make_shared<OpusPacketizer>(mRunLoop, mAudioSource);
packetizer->run();
@@ -136,19 +138,6 @@
return packetizer;
}
-size_t ServerState::acquireHandlerId() {
- size_t id = 0;
- while (!mAllocatedHandlerIds.insert(id).second) {
- ++id;
- }
-
- return id;
-}
-
-void ServerState::releaseHandlerId(size_t id) {
- CHECK_EQ(mAllocatedHandlerIds.erase(id), 1);
-}
-
std::shared_ptr<android::TouchSink> ServerState::getTouchSink() {
return mTouchSink;
}
diff --git a/host/frontend/gcastv2/webrtc/assets/js/app.js b/host/frontend/gcastv2/webrtc/assets/js/app.js
deleted file mode 100644
index f82d91d..0000000
--- a/host/frontend/gcastv2/webrtc/assets/js/app.js
+++ /dev/null
@@ -1,176 +0,0 @@
-'use strict';
-
-const receiveButton = document.getElementById('receiveButton');
-receiveButton.addEventListener('click', onReceive);
-const keyboardCaptureButton = document.getElementById('keyboardCaptureBtn');
-keyboardCaptureButton.addEventListener('click', onKeyboardCaptureClick);
-
-const deviceScreen = document.getElementById('deviceScreen');
-
-deviceScreen.addEventListener("click", onInitialClick);
-
-function onInitialClick(e) {
- // This stupid thing makes sure that we disable controls after the first click...
- // Why not just disable controls altogether you ask? Because then audio won't play
- // because these days user-interaction is required to enable audio playback...
- console.log("onInitialClick");
-
- deviceScreen.controls = false;
- deviceScreen.removeEventListener("click", onInitialClick);
-}
-
-let videoStream;
-
-let mouseIsDown = false;
-
-let deviceConnection;
-
-function onKeyboardCaptureClick(e) {
- const selectedClass = 'selected';
- if (keyboardCaptureButton.classList.contains(selectedClass)) {
- stopKeyboardTracking();
- keyboardCaptureButton.classList.remove(selectedClass);
- } else {
- startKeyboardTracking();
- keyboardCaptureButton.classList.add(selectedClass);
- }
-}
-
-async function onReceive() {
- console.log('onReceive');
- receiveButton.disabled = true;
-
- // init_logcat();
-
- let options = {
- // temporarily disable audio to free ports in the server since it's only
- // producing silence anyways.
- disable_audio: true,
- wsProtocol: (location.protocol == "http:") ? "ws:" : "wss:",
- wsPath: location.host + "/control",
- };
- let urlParams = new URLSearchParams(location.search);
- for (const [key, value] of urlParams) {
- options[key] = JSON.parse(value);
- }
-
- import('./cf_webrtc.js')
- .then(webrtcModule => webrtcModule.Connect('device_id', options))
- .then(devConn => {
- deviceConnection = devConn;
- videoStream = devConn.getVideoStream(0);
- deviceScreen.srcObject = videoStream;
- startMouseTracking(); // TODO stopMouseTracking() when disconnected
- });
-}
-
-function startMouseTracking() {
- if (window.PointerEvent) {
- deviceScreen.addEventListener("pointerdown", onStartDrag);
- deviceScreen.addEventListener("pointermove", onContinueDrag);
- deviceScreen.addEventListener("pointerup", onEndDrag);
- } else if (window.TouchEvent) {
- deviceScreen.addEventListener("touchstart", onStartDrag);
- deviceScreen.addEventListener("touchmove", onContinueDrag);
- deviceScreen.addEventListener("touchend", onEndDrag);
- } else if (window.MouseEvent) {
- deviceScreen.addEventListener("mousedown", onStartDrag);
- deviceScreen.addEventListener("mousemove", onContinueDrag);
- deviceScreen.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);
- }
-}
-
-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;
-
- sendMouseUpdate(true, e);
-}
-
-function onEndDrag(e) {
- e.preventDefault();
-
- // console.log("mouseup at " + e.pageX + " / " + e.pageY);
- mouseIsDown = false;
-
- sendMouseUpdate(false, e);
-}
-
-function onContinueDrag(e) {
- e.preventDefault();
-
- // console.log("mousemove at " + e.pageX + " / " + e.pageY + ", down=" + mouseIsDown);
- if (mouseIsDown) {
- sendMouseUpdate(true, e);
- }
-}
-
-function sendMouseUpdate(down, e) {
- console.assert(deviceConnection, 'Can\'t send mouse update without device');
- var x = e.offsetX;
- var y = e.offsetY;
-
- // TODO get the device's screen resolution from the device config, not the video stream
- const videoWidth = deviceScreen.videoWidth;
- const videoHeight = deviceScreen.videoHeight;
- const elementWidth = deviceScreen.offsetWidth;
- const elementHeight = deviceScreen.offsetHeight;
-
- // vh*ew > eh*vw? then scale h instead of w
- const scaleHeight = videoHeight * elementWidth > videoWidth * elementHeight;
- var elementScaling = 0, videoScaling = 0;
- if (scaleHeight) {
- elementScaling = elementHeight;
- videoScaling = videoHeight;
- } else {
- elementScaling = elementWidth;
- videoScaling = videoWidth;
- }
-
- // Substract the offset produced by the difference in aspect ratio if any.
- if (scaleHeight) {
- x -= (elementWidth - elementScaling * videoWidth / videoScaling) / 2;
- } else {
- y -= (elementHeight - elementScaling * videoHeight / videoScaling) / 2;
- }
-
- // Convert to coordinates relative to the video
- x = videoScaling * x / elementScaling;
- y = videoScaling * y / elementScaling;
-
- deviceConnection.sendMousePosition({x: Math.trunc(x), y: Math.trunc(y), down});
-}
-
-function onKeyEvent(e) {
- e.preventDefault();
- console.assert(deviceConnection, 'Can\'t send key event without device');
- deviceConnection.sendKeyEvent(e.code, e.type);
-}
diff --git a/host/frontend/gcastv2/webrtc/assets/js/cf_webrtc.js b/host/frontend/gcastv2/webrtc/assets/js/cf_webrtc.js
deleted file mode 100644
index 2b9499f..0000000
--- a/host/frontend/gcastv2/webrtc/assets/js/cf_webrtc.js
+++ /dev/null
@@ -1,302 +0,0 @@
-function createInputDataChannelPromise(pc) {
- console.log("creating data channel");
- let inputChannel = pc.createDataChannel('input-channel');
- return new Promise((resolve, reject) => {
- inputChannel.onopen = (event) => {
- resolve(inputChannel);
- };
- inputChannel.onclose = () => {
- console.log(
- 'handleDataChannelStatusChange state=' + dataChannel.readyState);
- };
- inputChannel.onmessage = (msg) => {
- console.log('handleDataChannelMessage data="' + msg.data + '"');
- };
- inputChannel.onerror = err => {
- reject(err);
- };
- });
-}
-
-class DeviceConnection {
- constructor(pc, control) {
- this._pc = pc;
- this._control = control;
- this._inputChannelPr = createInputDataChannelPromise(pc);
- this._videoStreams = [];
-
- // Apparently, the only way to obtain the track and the stream at the
- // same time is by subscribing to this event.
- pc.addEventListener('track', e => {
- console.log('Got remote stream: ', e);
- if (e.track.kind === 'video') {
- this._videoStreams.push(e.streams[0]);
- }
- });
- }
-
- set description(desc) {
- this._description = desc;
- }
-
- get description() {
- return this._description;
- }
-
- getVideoStream(displayNum = 0) {
- return this._videoStreams[displayNum];
- }
-
- _sendJsonInput(evt) {
- this._inputChannelPr = this._inputChannelPr.then(inputChannel => {
- inputChannel.send(JSON.stringify(evt));
- return inputChannel;
- });
- }
-
- sendMousePosition({x, y, down, display = 0}) {
- this._sendJsonInput({
- type: 'mouse',
- down: down ? 1 : 0,
- x,
- y,
- });
- }
-
- sendMultiTouch({id, x, y, initialDown, slot, display = 0}) {
- this._sendJsonInput({
- type: 'multi-touch',
- id,
- x,
- y,
- initialDown: initialDown ? 1 : 0,
- slot,
- });
- }
-
- sendKeyEvent(code, type) {
- this._sendJsonInput({type: 'keyboard', keycode: code, event_type: type});
- }
-
- disconnect() {
- this._pc.close();
- }
-
- // TODO(b/148086548) adb
-}
-
-
-const GREETING_MSG_TYPE = 'hello';
-const OFFER_MSG_TYPE = 'offer';
-const ICE_CANDIDATE_MSG_TYPE = 'ice-candidate';
-
-
-class WebRTCControl {
- constructor(deviceId, {
- wsProtocol = 'wss',
- wsPath = '',
- disable_audio = false,
- bundle_tracks = false,
- use_tcp = true,
- }) {
- /*
- * Private attributes:
- *
- * _options
- *
- * _promiseResolvers = {
- * [GREETING_MSG_TYPE]: function to resolve the greeting response promise
- *
- * [OFFER_MSG_TYPE]: function to resolve the offer promise
- *
- * [ICE_CANDIDATE_MSG_TYPE]: function to resolve the ice candidate
- * promise
- * }
- *
- * _wsPromise: promises the underlying websocket, should resolve when the
- * socket passes to OPEN state, will be rejecte/replaced by a
- * rejected promise if an error is detected on the socket.
- *
- */
-
- this._options = {
- disable_audio,
- bundle_tracks,
- use_tcp,
- };
-
- this._promiseResolvers = {};
-
- this._wsPromise = new Promise((resolve, reject) => {
- const wsUrl = `${wsProtocol}//${wsPath}`;
- let ws = new WebSocket(wsUrl);
- ws.onopen = () => {
- console.info('Websocket connected');
- resolve(ws);
- };
- ws.onerror = evt => {
- console.error('WebSocket error:', evt);
- reject(evt);
- // If the promise was already resolved the previous line has no effect
- this._wsPromise = Promise.reject(new Error(evt));
- };
- ws.onmessage = e => {
- console.log('onmessage ' + e.data);
- let data = JSON.parse(e.data);
- this._onWebsocketMessage(data);
- };
- });
- }
-
- _onWebsocketMessage(message) {
- const type = message.type;
- if (!(type in this._promiseResolvers)) {
- console.warn(`Unexpected message of type: ${type}`);
- return;
- }
- this._promiseResolvers[type](message);
- delete this._promiseResolvers[type];
- }
-
- async _wsSendJson(obj) {
- let ws = await this._wsPromise;
- return ws.send(JSON.stringify(obj));
- }
-
- /**
- * Send a greeting to the device, returns a promise of a greeting response.
- */
- async greet() {
- console.log('greet');
- return new Promise((resolve, reject) => {
- if (GREETING_MSG_TYPE in this._promiseResolvers) {
- const msg = 'Greeting already sent and not yet responded';
- console.error(msg);
- throw new Error(msg);
- }
- this._promiseResolvers[GREETING_MSG_TYPE] = resolve;
-
- this._wsSendJson({
- type: 'greeting',
- message: 'Hello, world!',
- options: this._options,
- });
- });
- }
-
- /**
- * Sends an offer request to the device, returns a promise of an offer.
- */
- async requestOffer() {
- console.log('requestOffer');
- return new Promise((resolve, reject) => {
- if (OFFER_MSG_TYPE in this._promiseResolvers) {
- const msg = 'Offer already requested and not yet received';
- console.error(msg);
- throw new Error(msg);
- }
- this._promiseResolvers[OFFER_MSG_TYPE] = resolve;
-
- const is_chrome = navigator.userAgent.indexOf('Chrome') !== -1;
- this._wsSendJson({type: 'request-offer', is_chrome: is_chrome ? 1 : 0});
- });
- }
-
- /**
- * Sends a remote description to the device.
- */
- async sendClientDescription(desc) {
- console.log('sendClientDescription');
- this._wsSendJson({type: 'set-client-desc', sdp: desc.sdp});
- }
-
- /**
- * Request an ICE candidate from the device. Returns a promise of an ice
- * candidate.
- */
- async requestICECandidate(mid) {
- console.log(`requestICECandidate(${mid})`);
- if (ICE_CANDIDATE_MSG_TYPE in this._promiseResolvers) {
- const msg = 'An ice candidate request is already pending';
- console.error(msg);
- throw new Error(msg);
- }
-
- let iceCandidatePromise = new Promise((resolve, reject) => {
- this._promiseResolvers[ICE_CANDIDATE_MSG_TYPE] = resolve;
- });
- this._wsSendJson({type: 'get-ice-candidate', mid: mid});
-
- let reply = await iceCandidatePromise;
- console.log('got reply: ', reply);
-
- if (reply == undefined || reply.candidate == undefined) {
- console.warn('Undefined reply or candidate');
- return null;
- }
-
- const replyCandidate = reply.candidate;
- const mlineIndex = reply.mlineIndex;
-
- const result = new RTCIceCandidate(
- {sdpMid: mid, sdpMLineIndex: mlineIndex, candidate: replyCandidate});
-
- console.log('got result: ', result);
-
- return result;
- }
-}
-
-function createPeerConnection() {
- let pc = new RTCPeerConnection();
- console.log('got pc2=', pc);
-
- pc.addEventListener('icecandidate', e => {
- console.log('pc.onIceCandidate: ', e.candidate);
- });
-
- pc.addEventListener(
- 'iceconnectionstatechange',
- e => console.log(`Ice State Change: ${pc.iceConnectionState}`));
-
- pc.addEventListener('connectionstatechange', e => {
- console.log('connection state = ' + pc.connectionState);
- });
-
- return pc;
-}
-
-export async function Connect(deviceId, options) {
- let control = new WebRTCControl(deviceId, options);
- let pc = createPeerConnection();
- let deviceConnection = new DeviceConnection(pc, control);
- try {
- let greetResponse = await control.greet();
- console.log('Greeting response: ', greetResponse);
- // TODO(jemoreira): get the description from the device
- deviceConnection.description = {};
-
- let desc = await control.requestOffer();
- console.log('Offer: ', desc);
- await pc.setRemoteDescription(desc);
-
- let answer = await pc.createAnswer();
- console.log('Answer: ', answer);
- // nest then() calls here to have easy access to the answer
- await pc.setLocalDescription(answer);
-
- await control.sendClientDescription(answer);
-
- for (let mid = 0; mid < 3; ++mid) {
- let iceCandidate = await control.requestICECandidate(mid);
- console.log(`ICE Candidate[${mid}]: `, iceCandidate);
- if (iceCandidate) await pc.addIceCandidate(iceCandidate);
- }
-
- console.log('WebRTC connection established');
- return deviceConnection;
- } catch (e) {
- console.error('Error establishing WebRTC connection: ', e);
- throw e;
- };
-}
diff --git a/host/frontend/gcastv2/webrtc/assets/style.css b/host/frontend/gcastv2/webrtc/assets/style.css
deleted file mode 100644
index 272b5f0..0000000
--- a/host/frontend/gcastv2/webrtc/assets/style.css
+++ /dev/null
@@ -1,39 +0,0 @@
-body {
- background-color:black
-}
-
-.noscroll {
- touch-action: none;
-}
-
-.one {
- float: left;
-}
-
-#logcat {
- display: none;
- font-family: monospace;
- padding: 10px;
-}
-
-button.selected {
- background-color: #aaaaaa;
- border-style: solid;
- border-color: #aaaaaa;
-}
-
-
-.noscroll {
- text-align: center;
-}
-
-section.noscroll div.one {
- display: inline-block;
- width: 100%;
- height: 90%;
-}
-
-#deviceScreen {
- max-width: 100%;
- max-height: 100%;
-}
diff --git a/host/frontend/gcastv2/webrtc/certs/server.crt b/host/frontend/gcastv2/webrtc/certs/server.crt
deleted file mode 100644
index 81759be..0000000
--- a/host/frontend/gcastv2/webrtc/certs/server.crt
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDTDCCAjQCCQCsLGBNpNbWCjANBgkqhkiG9w0BAQsFADBoMQswCQYDVQQGEwJV
-UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAY
-BgNVBAoMEUJleW9uZCBBZ2dyYXZhdGVkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcN
-MTkwNTA4MjAxMTQ5WhcNMjAwNTA3MjAxMTQ5WjBoMQswCQYDVQQGEwJVUzETMBEG
-A1UECAwKQ2FsaWZvcm5pYTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNVBAoM
-EUJleW9uZCBBZ2dyYXZhdGVkMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQD3c9E6JtTUro8MWBYRtjJ6k01ORfROTsJz
-vbL5V3IV6MyUqGKbhRuN2SP/XsVZoUCmeFsCVF++G3mLmYetdUtb/ZwIgF/sN5aW
-oh//YDR/T2kepMjk73kvQ7R6h5bB5vQIbpbP+piJR1S7HxlXEgwyLuXmKgVNF6tU
-WJt/vRFLRDiz0Bn3GklE5yOgJGeeU3CYioatAiKE6b/yPu5pcM7Kj4tn9hu5COO2
-89Wol3od+I3pSkd9tXypdfw1txdAi0o9P7uIDIDSIDwf7RyCoxojZ7yUxO2Q0ld+
-KFJqioSpFIscIxR1BaTIrJaSCU7Hn82ihODS2ue4OaoAJ2qH8JtVAgMBAAEwDQYJ
-KoZIhvcNAQELBQADggEBAPQ+7bXvy/RSx9lgSAiO/R5ep3Si2xWUe0hYivmQ1bX2
-80FpDP8tZXtAkIhpyWdiS0aYBMySLDDDiDl2xUWfxZO0NnOcVJ17jZ2sJxhqKksW
-NMTLb7dCr7kUS2+FOuXwR+Yeb77up2e54lXLuiKVWevFAUVc8Xhgq/sNz2rwt5iG
-XFcLCXoEgLwHnd7LBR8y6IsEfVW5UVSWpFQPODdcYtVgaWYo7TYghZjzEya8VIc7
-HgHlH/1Uj9yvh+eY2hLoLwukZRV/CnMbSk8LLgTYuEeLfPuSnCTERrydMEyRcF3H
-ljCthgV7YRt8cQovsVZBvuMRJYzl4hM3ema00Px7SD8=
------END CERTIFICATE-----
diff --git a/host/frontend/gcastv2/webrtc/certs/server.p12 b/host/frontend/gcastv2/webrtc/certs/server.p12
deleted file mode 100644
index 3d0d595..0000000
--- a/host/frontend/gcastv2/webrtc/certs/server.p12
+++ /dev/null
Binary files differ
diff --git a/host/frontend/gcastv2/webrtc/certs/trusted.pem b/host/frontend/gcastv2/webrtc/certs/trusted.pem
deleted file mode 100644
index b2080b4..0000000
--- a/host/frontend/gcastv2/webrtc/certs/trusted.pem
+++ /dev/null
@@ -1,69 +0,0 @@
-Certificate:
- Data:
- Version: 1 (0x0)
- Serial Number: 12406396960093165066 (0xac2c604da4d6d60a)
- Signature Algorithm: sha256WithRSAEncryption
- Issuer: C=US, ST=California, L=Santa Clara, O=Beyond Aggravated, CN=localhost
- Validity
- Not Before: May 8 20:11:49 2019 GMT
- Not After : May 7 20:11:49 2020 GMT
- Subject: C=US, ST=California, L=Santa Clara, O=Beyond Aggravated, CN=localhost
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- 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
- Exponent: 65537 (0x10001)
- Signature Algorithm: sha256WithRSAEncryption
- f4:3e:ed:b5:ef:cb:f4:52:c7:d9:60:48:08:8e:fd:1e:5e:a7:
- 74:a2:db:15:94:7b:48:58:8a:f9:90:d5:b5:f6:f3:41:69:0c:
- ff:2d:65:7b:40:90:88:69:c9:67:62:4b:46:98:04:cc:92:2c:
- 30:c3:88:39:76:c5:45:9f:c5:93:b4:36:73:9c:54:9d:7b:8d:
- 9d:ac:27:18:6a:2a:4b:16:34:c4:cb:6f:b7:42:af:b9:14:4b:
- 6f:85:3a:e5:f0:47:e6:1e:6f:be:ee:a7:67:b9:e2:55:cb:ba:
- 22:95:59:eb:c5:01:45:5c:f1:78:60:ab:fb:0d:cf:6a:f0:b7:
- 98:86:5c:57:0b:09:7a:04:80:bc:07:9d:de:cb:05:1f:32:e8:
- 8b:04:7d:55:b9:51:54:96:a4:54:0f:38:37:5c:62:d5:60:69:
- 66:28:ed:36:20:85:98:f3:13:26:bc:54:87:3b:1e:01:e5:1f:
- fd:54:8f:dc:af:87:e7:98:da:12:e8:2f:0b:a4:65:15:7f:0a:
- 73:1b:4a:4f:0b:2e:04:d8:b8:47:8b:7c:fb:92:9c:24:c4:46:
- bc:9d:30:4c:91:70:5d:c7:96:30:ad:86:05:7b:61:1b:7c:71:
- 0a:2f:b1:56:41:be:e3:11:25:8c:e5:e2:13:37:7a:66:b4:d0:
- fc:7b:48:3f
------BEGIN CERTIFICATE-----
-MIIDTDCCAjQCCQCsLGBNpNbWCjANBgkqhkiG9w0BAQsFADBoMQswCQYDVQQGEwJV
-UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAY
-BgNVBAoMEUJleW9uZCBBZ2dyYXZhdGVkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcN
-MTkwNTA4MjAxMTQ5WhcNMjAwNTA3MjAxMTQ5WjBoMQswCQYDVQQGEwJVUzETMBEG
-A1UECAwKQ2FsaWZvcm5pYTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNVBAoM
-EUJleW9uZCBBZ2dyYXZhdGVkMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQD3c9E6JtTUro8MWBYRtjJ6k01ORfROTsJz
-vbL5V3IV6MyUqGKbhRuN2SP/XsVZoUCmeFsCVF++G3mLmYetdUtb/ZwIgF/sN5aW
-oh//YDR/T2kepMjk73kvQ7R6h5bB5vQIbpbP+piJR1S7HxlXEgwyLuXmKgVNF6tU
-WJt/vRFLRDiz0Bn3GklE5yOgJGeeU3CYioatAiKE6b/yPu5pcM7Kj4tn9hu5COO2
-89Wol3od+I3pSkd9tXypdfw1txdAi0o9P7uIDIDSIDwf7RyCoxojZ7yUxO2Q0ld+
-KFJqioSpFIscIxR1BaTIrJaSCU7Hn82ihODS2ue4OaoAJ2qH8JtVAgMBAAEwDQYJ
-KoZIhvcNAQELBQADggEBAPQ+7bXvy/RSx9lgSAiO/R5ep3Si2xWUe0hYivmQ1bX2
-80FpDP8tZXtAkIhpyWdiS0aYBMySLDDDiDl2xUWfxZO0NnOcVJ17jZ2sJxhqKksW
-NMTLb7dCr7kUS2+FOuXwR+Yeb77up2e54lXLuiKVWevFAUVc8Xhgq/sNz2rwt5iG
-XFcLCXoEgLwHnd7LBR8y6IsEfVW5UVSWpFQPODdcYtVgaWYo7TYghZjzEya8VIc7
-HgHlH/1Uj9yvh+eY2hLoLwukZRV/CnMbSk8LLgTYuEeLfPuSnCTERrydMEyRcF3H
-ljCthgV7YRt8cQovsVZBvuMRJYzl4hM3ema00Px7SD8=
------END CERTIFICATE-----
diff --git a/host/frontend/gcastv2/webrtc/MyWebSocketHandler.cpp b/host/frontend/gcastv2/webrtc/client_handler.cpp
similarity index 61%
rename from host/frontend/gcastv2/webrtc/MyWebSocketHandler.cpp
rename to host/frontend/gcastv2/webrtc/client_handler.cpp
index e48acbd..703cdf7 100644
--- a/host/frontend/gcastv2/webrtc/MyWebSocketHandler.cpp
+++ b/host/frontend/gcastv2/webrtc/client_handler.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-#include <webrtc/MyWebSocketHandler.h>
+#include <webrtc/client_handler.h>
+
+#include <vector>
#include "Utils.h"
@@ -23,6 +25,9 @@
#include <netdb.h>
#include <openssl/rand.h>
+#include "https/SafeCallbackable.h"
+#include "common/libs/utils/base64.h"
+
namespace {
// helper method to ensure a json object has the required fields convertible
@@ -54,79 +59,87 @@
} // namespace
-MyWebSocketHandler::MyWebSocketHandler(
- std::shared_ptr<RunLoop> runLoop,
+ClientHandler::ClientHandler(
std::shared_ptr<ServerState> serverState,
- size_t handlerId)
- : mRunLoop(runLoop),
+ std::function<void(const Json::Value&)> send_to_client_cb)
+ : mRunLoop(serverState->run_loop()),
mServerState(serverState),
- mId(handlerId),
mOptions(OptionBits::useSingleCertificateForAllTracks
| OptionBits::enableData),
+ sendToClient_(send_to_client_cb),
mTouchSink(mServerState->getTouchSink()),
mKeyboardSink(mServerState->getKeyboardSink()) {
}
-MyWebSocketHandler::~MyWebSocketHandler() {
- mServerState->releaseHandlerId(mId);
+std::shared_ptr<AdbHandler> ClientHandler::adb_handler() {
+ if (!adb_handler_) {
+ auto config = vsoc::CuttlefishConfig::Get();
+ adb_handler_.reset(
+ new AdbHandler(mRunLoop, config->ForDefaultInstance().adb_ip_and_port(),
+ [this](const uint8_t *msg, size_t length) {
+ std::string base64_msg;
+ cvd::EncodeBase64(msg, length, &base64_msg);
+ Json::Value reply;
+ reply["type"] = "adb-message";
+ reply["payload"] = base64_msg;
+ sendToClient_(reply);
+ }));
+ adb_handler_->run();
+ }
+ return adb_handler_;
}
-int MyWebSocketHandler::handleMessage(
- uint8_t /*headerByte*/, const uint8_t *msg, size_t len) {
- // android::hexdump(msg, len);
+void ClientHandler::LogAndReplyError(const std::string& error_msg) const {
+ LOG(ERROR) << error_msg;
+ Json::Value reply;
+ reply["error"] = error_msg;
+ sendToClient_(reply);
+}
- Json::Value obj;
- Json::Reader json_reader;
- Json::FastWriter json_writer;
- auto str = reinterpret_cast<const char *>(msg);
- if (!json_reader.parse(str, str + len, obj) < 0) {
- return -EINVAL;
+void ClientHandler::HandleMessage(const Json::Value& message) {
+ LOG(VERBOSE) << message.toStyledString();
+
+ if (!validateJsonObject(
+ message, "", {{"type", Json::ValueType::stringValue}},
+ [this](const std::string &error) { LogAndReplyError(error); })) {
+ return;
}
-
- LOG(VERBOSE) << obj.toStyledString();
-
- auto sendMessageOnError =
- [this](const std::string &error_msg) {
- auto reply = "{\"error\": \"" + error_msg + "\"}";
- sendMessage(reply.c_str(), reply.size());
- };
-
- if (!validateJsonObject(obj, "", {{"type", Json::ValueType::stringValue}},
- sendMessageOnError)) {
- return -EINVAL;
- }
- std::string type = obj["type"].asString();
-
- if (type == "greeting") {
- Json::Value reply;
- reply["type"] = "hello";
- reply["reply"] = "Right back at ya!";
-
- auto replyAsString = json_writer.write(reply);
- sendMessage(replyAsString.c_str(), replyAsString.size());
-
- if (obj.isMember("options")) {
- parseOptions(obj["options"]);
+ auto type = message["type"].asString();
+ if (type == "request-offer") {
+ if (message.isMember("options")) {
+ parseOptions(message["options"]);
}
-
if (mOptions & OptionBits::useSingleCertificateForAllTracks) {
mCertificateAndKey = CreateDTLSCertificateAndKey();
}
-
prepareSessions();
- } else if (type == "set-client-desc") {
- if (!validateJsonObject(obj, type,
+ auto offer = BuildOffer();
+
+ Json::Value reply;
+ reply["type"] = "offer";
+ reply["sdp"] = offer;
+
+ sendToClient_(reply);
+ } else if (type == "answer") {
+ if (mSessions.size() == 0) {
+ LOG(ERROR)
+ << "Received sdp answer from client before request for offer";
+ return;
+ }
+
+ if (!validateJsonObject(message, type,
{{"sdp", Json::ValueType::stringValue}},
- sendMessageOnError)) {
- return -EINVAL;
+ [this](const std::string &error) {
+ LogAndReplyError(error);
+ })) {
+ return;
}
- int err = mOfferedSDP.setTo(obj["sdp"].asString());
-
+ int err = mOfferedSDP.setTo(message["sdp"].asString());
if (err) {
- LOG(ERROR) << "Offered SDP could not be parsed (" << err << ")";
+ LogAndReplyError("Offered SDP could not be parsed (" +
+ std::to_string(err) + ")");
}
-
for (size_t i = 0; i < mSessions.size(); ++i) {
const auto &session = mSessions[i];
@@ -136,155 +149,148 @@
getRemoteFingerprint(i));
}
- return err;
- } else if (type == "request-offer") {
- std::stringstream ss;
-
- ss <<
-"v=0\r\n"
-"o=- 7794515898627856655 2 IN IP4 127.0.0.1\r\n"
-"s=-\r\n"
-"t=0 0\r\n"
-"a=msid-semantic: WMS pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw\r\n";
-
- bool bundled = false;
-
- if ((mOptions & OptionBits::bundleTracks) && countTracks() > 1) {
- bundled = true;
-
- ss << "a=group:BUNDLE 0";
-
- if (!(mOptions & OptionBits::disableAudio)) {
- ss << " 1";
- }
-
- if (mOptions & OptionBits::enableData) {
- ss << " 2";
- }
-
- ss << "\r\n";
-
- emitTrackIceOptionsAndFingerprint(ss, 0 /* mlineIndex */);
+ for (int32_t mid = 0; mid < 3; mid++) {
+ GatherAndSendCandidate(mid);
}
-
- size_t mlineIndex = 0;
-
- // Video track (mid = 0)
-
- std::string videoEncodingSpecific = "a=rtpmap:96 VP8/90000\r\n";
-
- videoEncodingSpecific +=
-"a=rtcp-fb:96 ccm fir\r\n"
-"a=rtcp-fb:96 nack\r\n"
-"a=rtcp-fb:96 nack pli\r\n";
-
- ss <<
-"m=video 9 "
-<< ((mOptions & OptionBits::useTCP) ? "TCP" : "UDP")
-<< "/TLS/RTP/SAVPF 96 97\r\n"
-"c=IN IP4 0.0.0.0\r\n"
-"a=rtcp:9 IN IP4 0.0.0.0\r\n";
-
- if (!bundled) {
- emitTrackIceOptionsAndFingerprint(ss, mlineIndex++);
- }
-
- ss <<
-"a=setup:actpass\r\n"
-"a=mid:0\r\n"
-"a=sendonly\r\n"
-"a=rtcp-mux\r\n"
-"a=rtcp-rsize\r\n"
-"a=rtcp-xr:rcvr-rtt=all\r\n";
-
- ss << videoEncodingSpecific <<
-"a=rtpmap:97 rtx/90000\r\n"
-"a=fmtp:97 apt=96\r\n"
-"a=ssrc-group:FID 3735928559 3405689008\r\n"
-"a=ssrc:3735928559 cname:myWebRTP\r\n"
-"a=ssrc:3735928559 msid:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw 61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n"
-"a=ssrc:3735928559 mslabel:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw\r\n"
-"a=ssrc:3735928559 label:61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n"
-"a=ssrc:3405689008 cname:myWebRTP\r\n"
-"a=ssrc:3405689008 msid:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw 61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n"
-"a=ssrc:3405689008 mslabel:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw\r\n"
-"a=ssrc:3405689008 label:61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n";
-
- if (!(mOptions & OptionBits::disableAudio)) {
- ss <<
-"m=audio 9 "
-<< ((mOptions & OptionBits::useTCP) ? "TCP" : "UDP")
-<< "/TLS/RTP/SAVPF 98\r\n"
-"c=IN IP4 0.0.0.0\r\n"
-"a=rtcp:9 IN IP4 0.0.0.0\r\n";
-
- if (!bundled) {
- emitTrackIceOptionsAndFingerprint(ss, mlineIndex++);
- }
-
- ss <<
-"a=setup:actpass\r\n"
-"a=mid:1\r\n"
-"a=sendonly\r\n"
-"a=msid:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw 61843856-edd7-4ca9-be79-4e3ccc6cc035\r\n"
-"a=rtcp-mux\r\n"
-"a=rtcp-rsize\r\n"
-"a=rtpmap:98 opus/48000/2\r\n"
-"a=fmtp:98 minptime=10;useinbandfec=1\r\n"
-"a=ssrc-group:FID 2343432205\r\n"
-"a=ssrc:2343432205 cname:myWebRTP\r\n"
-"a=ssrc:2343432205 msid:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw 61843856-edd7-4ca9-be79-4e3ccc6cc035\r\n"
-"a=ssrc:2343432205 mslabel:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw\r\n"
-"a=ssrc:2343432205 label:61843856-edd7-4ca9-be79-4e3ccc6cc035\r\n";
- }
-
- if (mOptions & OptionBits::enableData) {
- ss <<
-"m=application 9 "
-<< ((mOptions & OptionBits::useTCP) ? "TCP" : "UDP")
-<< "/DTLS/SCTP webrtc-datachannel\r\n"
-"c=IN IP4 0.0.0.0\r\n"
-"a=sctp-port:5000\r\n";
-
- if (!bundled) {
- emitTrackIceOptionsAndFingerprint(ss, mlineIndex++);
- }
-
- ss <<
-"a=setup:actpass\r\n"
-"a=mid:2\r\n"
-"a=sendrecv\r\n"
-"a=fmtp:webrtc-datachannel max-message-size=65536\r\n";
- }
-
- Json::Value reply;
- reply["type"] = "offer";
- reply["sdp"] = ss.str();
-
- auto replyAsString = json_writer.write(reply);
- sendMessage(replyAsString.c_str(), replyAsString.size());
- } else if (type == "get-ice-candidate") {
- if (!validateJsonObject(obj, type, {{"mid", Json::ValueType::intValue}},
- sendMessageOnError)) {
- return -EINVAL;
- }
- int32_t mid = obj["mid"].asInt();
-
- bool success = getCandidate(mid);
-
- if (!success) {
- Json::Value reply;
- reply["type"] = "ice-candidate";
-
- auto replyAsString = json_writer.write(reply);
- sendMessage(replyAsString.c_str(), replyAsString.size());
- }
+ } else if (type == "ice-candidate") {
+ LOG(INFO) << "Received ice candidate from client, ignoring";
+ } else if (type == "adb-message") {
+ if (!message.isMember("payload") || !message["payload"].isString()) {
+ LOG(ERROR) << "adb-message has invalid payload";
+ return;
+ }
+ auto base64_msg = message["payload"].asString();
+ std::vector<uint8_t> raw_msg;
+ if (!cvd::DecodeBase64(base64_msg, &raw_msg)) {
+ LOG(ERROR) << "Invalid base64 string in adb-message";
+ return;
+ }
+ adb_handler()->handleMessage(raw_msg.data(), raw_msg.size());
+ } else {
+ LogAndReplyError("Unknown type: " + type);
+ return;
}
-
- return 0;
}
-size_t MyWebSocketHandler::countTracks() const {
+std::string ClientHandler::BuildOffer() {
+ std::stringstream ss;
+
+ ss << "v=0\r\n"
+ "o=- 7794515898627856655 2 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "t=0 0\r\n"
+ "a=msid-semantic: WMS display_0\r\n";
+
+ bool bundled = false;
+
+ if ((mOptions & OptionBits::bundleTracks) && countTracks() > 1) {
+ bundled = true;
+
+ ss << "a=group:BUNDLE 0";
+
+ if (!(mOptions & OptionBits::disableAudio)) {
+ ss << " 1";
+ }
+
+ if (mOptions & OptionBits::enableData) {
+ ss << " 2";
+ }
+
+ ss << "\r\n";
+
+ emitTrackIceOptionsAndFingerprint(ss, 0 /* mlineIndex */);
+ }
+
+ size_t mlineIndex = 0;
+
+ // Video track (mid = 0)
+
+ std::string videoEncodingSpecific = "a=rtpmap:96 VP8/90000\r\n";
+
+ videoEncodingSpecific +=
+ "a=rtcp-fb:96 ccm fir\r\n"
+ "a=rtcp-fb:96 nack\r\n"
+ "a=rtcp-fb:96 nack pli\r\n";
+
+ ss << "m=video 9 " << ((mOptions & OptionBits::useTCP) ? "TCP" : "UDP")
+ << "/TLS/RTP/SAVPF 96 97\r\n"
+ "c=IN IP4 0.0.0.0\r\n"
+ "a=rtcp:9 IN IP4 0.0.0.0\r\n";
+
+ if (!bundled) {
+ emitTrackIceOptionsAndFingerprint(ss, mlineIndex++);
+ }
+
+ ss << "a=setup:actpass\r\n"
+ "a=mid:0\r\n"
+ "a=sendonly\r\n"
+ "a=rtcp-mux\r\n"
+ "a=rtcp-rsize\r\n"
+ "a=rtcp-xr:rcvr-rtt=all\r\n";
+
+ ss << videoEncodingSpecific
+ << "a=rtpmap:97 rtx/90000\r\n"
+ "a=fmtp:97 apt=96\r\n"
+ "a=ssrc-group:FID 3735928559 3405689008\r\n"
+ "a=ssrc:3735928559 cname:myWebRTP\r\n"
+ "a=ssrc:3735928559 msid:display_0 "
+ "61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n"
+ "a=ssrc:3735928559 mslabel:display_0\r\n"
+ "a=ssrc:3735928559 label:61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n"
+ "a=ssrc:3405689008 cname:myWebRTP\r\n"
+ "a=ssrc:3405689008 msid:display_0 "
+ "61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n"
+ "a=ssrc:3405689008 mslabel:display_0\r\n"
+ "a=ssrc:3405689008 label:61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n";
+
+ if (!(mOptions & OptionBits::disableAudio)) {
+ ss << "m=audio 9 " << ((mOptions & OptionBits::useTCP) ? "TCP" : "UDP")
+ << "/TLS/RTP/SAVPF 98\r\n"
+ "c=IN IP4 0.0.0.0\r\n"
+ "a=rtcp:9 IN IP4 0.0.0.0\r\n";
+
+ if (!bundled) {
+ emitTrackIceOptionsAndFingerprint(ss, mlineIndex++);
+ }
+
+ ss << "a=setup:actpass\r\n"
+ "a=mid:1\r\n"
+ "a=sendonly\r\n"
+ "a=msid:display_0 "
+ "61843856-edd7-4ca9-be79-4e3ccc6cc035\r\n"
+ "a=rtcp-mux\r\n"
+ "a=rtcp-rsize\r\n"
+ "a=rtpmap:98 opus/48000/2\r\n"
+ "a=fmtp:98 minptime=10;useinbandfec=1\r\n"
+ "a=ssrc-group:FID 2343432205\r\n"
+ "a=ssrc:2343432205 cname:myWebRTP\r\n"
+ "a=ssrc:2343432205 msid:display_0 "
+ "61843856-edd7-4ca9-be79-4e3ccc6cc035\r\n"
+ "a=ssrc:2343432205 mslabel:display_0\r\n"
+ "a=ssrc:2343432205 label:61843856-edd7-4ca9-be79-4e3ccc6cc035\r\n";
+ }
+
+ if (mOptions & OptionBits::enableData) {
+ ss << "m=application 9 "
+ << ((mOptions & OptionBits::useTCP) ? "TCP" : "UDP")
+ << "/DTLS/SCTP webrtc-datachannel\r\n"
+ "c=IN IP4 0.0.0.0\r\n"
+ "a=sctp-port:5000\r\n";
+
+ if (!bundled) {
+ emitTrackIceOptionsAndFingerprint(ss, mlineIndex++);
+ }
+
+ ss << "a=setup:actpass\r\n"
+ "a=mid:2\r\n"
+ "a=sendrecv\r\n"
+ "a=fmtp:webrtc-datachannel max-message-size=65536\r\n";
+ }
+ return ss.str();
+}
+
+
+size_t ClientHandler::countTracks() const {
size_t n = 1; // We always have a video track.
if (!(mOptions & OptionBits::disableAudio)) {
@@ -298,7 +304,7 @@
return n;
}
-ssize_t MyWebSocketHandler::mlineIndexForMid(int32_t mid) const {
+ssize_t ClientHandler::mlineIndexForMid(int32_t mid) const {
switch (mid) {
case 0:
return 0;
@@ -326,7 +332,7 @@
}
}
-bool MyWebSocketHandler::getCandidate(int32_t mid) {
+bool ClientHandler::GatherAndSendCandidate(int32_t mid) {
auto mlineIndex = mlineIndexForMid(mid);
if (mlineIndex < 0) {
@@ -371,16 +377,19 @@
trackMask,
session);
- rtp->run();
-
mRTPs.push_back(rtp);
+ rtp->OnParticipantDisconnected([this]{
+ mRunLoop->post(makeSafeCallback<ClientHandler>(
+ this,
+ [](ClientHandler *me) {
+ me->on_connection_closed_cb_();
+ }));
+ });
+ rtp->run();
}
auto rtp = mRTPs.back();
- Json::Value reply;
- reply["type"] = "ice-candidate";
-
auto localIPString = rtp->getLocalIPString();
std::stringstream ss;
@@ -401,17 +410,17 @@
ss << "generation 0 ufrag " << rtp->getLocalUFrag();
+ Json::Value reply;
+ reply["type"] = "ice-candidate";
+ reply["mid"] = mid;
+ reply["mLineIndex"] = static_cast<Json::UInt64>(mlineIndex);
reply["candidate"] = ss.str();
- reply["mlineIndex"] = static_cast<Json::UInt64>(mlineIndex);
- Json::FastWriter json_writer;
- auto replyAsString = json_writer.write(reply);
- sendMessage(replyAsString.c_str(), replyAsString.size());
-
+ sendToClient_(reply);
return true;
}
-std::optional<std::string> MyWebSocketHandler::getSDPValue(
+std::optional<std::string> ClientHandler::getSDPValue(
ssize_t targetMediaIndex,
std::string_view key,
bool fallthroughToGeneralSection) const {
@@ -464,21 +473,21 @@
return (*it).substr(prefix.size());
}
-std::string MyWebSocketHandler::getRemotePassword(size_t mlineIndex) const {
+std::string ClientHandler::getRemotePassword(size_t mlineIndex) const {
auto value = getSDPValue(
mlineIndex, "ice-pwd", true /* fallthroughToGeneralSection */);
return value ? *value : std::string();
}
-std::string MyWebSocketHandler::getRemoteUFrag(size_t mlineIndex) const {
+std::string ClientHandler::getRemoteUFrag(size_t mlineIndex) const {
auto value = getSDPValue(
mlineIndex, "ice-ufrag", true /* fallthroughToGeneralSection */);
return value ? *value : std::string();
}
-std::string MyWebSocketHandler::getRemoteFingerprint(size_t mlineIndex) const {
+std::string ClientHandler::getRemoteFingerprint(size_t mlineIndex) const {
auto value = getSDPValue(
mlineIndex, "fingerprint", true /* fallthroughToGeneralSection */);
@@ -487,7 +496,7 @@
// static
std::pair<std::shared_ptr<X509>, std::shared_ptr<EVP_PKEY>>
-MyWebSocketHandler::CreateDTLSCertificateAndKey() {
+ClientHandler::CreateDTLSCertificateAndKey() {
// Modeled after "https://stackoverflow.com/questions/256405/
// programmatically-create-x509-certificate-using-openssl".
@@ -542,7 +551,7 @@
return std::make_pair(x509, pkey);
}
-void MyWebSocketHandler::parseOptions(const Json::Value& options) {
+void ClientHandler::parseOptions(const Json::Value& options) {
if (options.isMember("disable_audio") && options["disable_audio"].isBool()) {
auto mask = OptionBits::disableAudio;
mOptions = (mOptions & ~mask) | (options["disable_audio"].asBool() ? mask : 0);
@@ -562,7 +571,7 @@
}
// static
-void MyWebSocketHandler::CreateRandomIceCharSequence(char *dst, size_t size) {
+void ClientHandler::CreateRandomIceCharSequence(char *dst, size_t size) {
// Per RFC 5245 an ice-char is alphanumeric, '+' or '/', i.e. 64 distinct
// character values (6 bit).
@@ -587,7 +596,7 @@
}
std::pair<std::string, std::string>
-MyWebSocketHandler::createUniqueUFragAndPassword() {
+ClientHandler::createUniqueUFragAndPassword() {
// RFC 5245, section 15.4 mandates that uFrag is at least 4 and password
// at least 22 ice-chars long.
@@ -618,7 +627,7 @@
std::string(passwordChars, sizeof(passwordChars)));
}
-void MyWebSocketHandler::prepareSessions() {
+void ClientHandler::prepareSessions() {
size_t numSessions =
(mOptions & OptionBits::bundleTracks) ? 1 : countTracks();
@@ -635,7 +644,7 @@
}
}
-void MyWebSocketHandler::emitTrackIceOptionsAndFingerprint(
+void ClientHandler::emitTrackIceOptionsAndFingerprint(
std::stringstream &ss, size_t mlineIndex) const {
CHECK_LT(mlineIndex, mSessions.size());
const auto &session = mSessions[mlineIndex];
diff --git a/host/frontend/gcastv2/webrtc/include/webrtc/AdbWebSocketHandler.h b/host/frontend/gcastv2/webrtc/include/webrtc/AdbHandler.h
similarity index 73%
rename from host/frontend/gcastv2/webrtc/include/webrtc/AdbWebSocketHandler.h
rename to host/frontend/gcastv2/webrtc/include/webrtc/AdbHandler.h
index 0a622b2..44ae35f 100644
--- a/host/frontend/gcastv2/webrtc/include/webrtc/AdbWebSocketHandler.h
+++ b/host/frontend/gcastv2/webrtc/include/webrtc/AdbHandler.h
@@ -21,20 +21,19 @@
#include <memory>
-struct AdbWebSocketHandler
- : public WebSocketHandler,
- public std::enable_shared_from_this<AdbWebSocketHandler> {
+struct AdbHandler :
+ public std::enable_shared_from_this<AdbHandler> {
- explicit AdbWebSocketHandler(
+ explicit AdbHandler(
std::shared_ptr<RunLoop> runLoop,
- const std::string &adb_host_and_port);
+ const std::string &adb_host_and_port,
+ std::function<void(const uint8_t*, size_t)> send_to_client);
- ~AdbWebSocketHandler() override;
+ ~AdbHandler();
void run();
- int handleMessage(
- uint8_t headerByte, const uint8_t *msg, size_t len) override;
+ void handleMessage(const uint8_t *msg, size_t len);
private:
struct AdbConnection;
@@ -44,6 +43,7 @@
int mSocket;
+ std::function<void(const uint8_t*, size_t)> send_to_client_;
+
int setupSocket(const std::string &adb_host_and_port);
};
-
diff --git a/host/frontend/gcastv2/webrtc/include/webrtc/MyWebSocketHandler.h b/host/frontend/gcastv2/webrtc/include/webrtc/MyWebSocketHandler.h
deleted file mode 100644
index cc70491..0000000
--- a/host/frontend/gcastv2/webrtc/include/webrtc/MyWebSocketHandler.h
+++ /dev/null
@@ -1,111 +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.
- */
-
-#pragma once
-
-#include <webrtc/RTPSession.h>
-#include <webrtc/RTPSocketHandler.h>
-#include <webrtc/SDP.h>
-#include <webrtc/ServerState.h>
-
-#include <https/WebSocketHandler.h>
-#include <https/RunLoop.h>
-#include <source/KeyboardSink.h>
-#include <source/TouchSink.h>
-
-#include <memory>
-#include <optional>
-#include <sstream>
-#include <string>
-#include <vector>
-
-struct MyWebSocketHandler
- : public WebSocketHandler,
- public std::enable_shared_from_this<MyWebSocketHandler> {
-
- explicit MyWebSocketHandler(
- std::shared_ptr<RunLoop> runLoop,
- std::shared_ptr<ServerState> serverState,
- size_t handlerId);
-
- ~MyWebSocketHandler() override;
-
- int handleMessage(
- uint8_t headerByte, const uint8_t *msg, size_t len) override;
-
-private:
- enum OptionBits : uint32_t {
- disableAudio = 1,
- bundleTracks = 2,
- enableData = 4,
- useSingleCertificateForAllTracks = 8,
- useTCP = 16,
- };
-
- using TouchSink = android::TouchSink;
- using KeyboardSink = android::KeyboardSink;
-
- std::shared_ptr<RunLoop> mRunLoop;
- std::shared_ptr<ServerState> mServerState;
- size_t mId;
- uint32_t mOptions;
-
- // Vector has the same ordering as the media entries in the SDP, i.e.
- // vector index is "mlineIndex". (unless we are bundling, in which case
- // there is only a single session).
- std::vector<std::shared_ptr<RTPSession>> mSessions;
-
- SDP mOfferedSDP;
- std::vector<std::shared_ptr<RTPSocketHandler>> mRTPs;
-
- std::shared_ptr<TouchSink> mTouchSink;
- std::shared_ptr<KeyboardSink> mKeyboardSink;
-
- std::pair<std::shared_ptr<X509>, std::shared_ptr<EVP_PKEY>>
- mCertificateAndKey;
-
- // Pass -1 for mlineIndex to access the "general" section.
- std::optional<std::string> getSDPValue(
- ssize_t mlineIndex,
- std::string_view key,
- bool fallthroughToGeneralSection) const;
-
- std::string getRemotePassword(size_t mlineIndex) const;
- std::string getRemoteUFrag(size_t mlineIndex) const;
- std::string getRemoteFingerprint(size_t mlineIndex) const;
-
- bool getCandidate(int32_t mid);
-
- static std::pair<std::shared_ptr<X509>, std::shared_ptr<EVP_PKEY>>
- CreateDTLSCertificateAndKey();
-
- std::pair<std::string, std::string> createUniqueUFragAndPassword();
-
- void parseOptions(const Json::Value& options);
- size_t countTracks() const;
-
- void prepareSessions();
-
- void emitTrackIceOptionsAndFingerprint(
- std::stringstream &ss, size_t mlineIndex) const;
-
- // Returns -1 on error.
- ssize_t mlineIndexForMid(int32_t mid) const;
-
- static void CreateRandomIceCharSequence(char *dst, size_t size);
-};
-
-
diff --git a/host/frontend/gcastv2/webrtc/include/webrtc/RTPSocketHandler.h b/host/frontend/gcastv2/webrtc/include/webrtc/RTPSocketHandler.h
index dbbf000..5cea620 100644
--- a/host/frontend/gcastv2/webrtc/include/webrtc/RTPSocketHandler.h
+++ b/host/frontend/gcastv2/webrtc/include/webrtc/RTPSocketHandler.h
@@ -67,6 +67,10 @@
void notifyDTLSConnected();
+ void OnParticipantDisconnected(std::function<void()> cb) {
+ on_participant_disconnected_ = cb;
+ }
+
private:
struct Datagram {
explicit Datagram(
@@ -109,6 +113,11 @@
std::shared_ptr<std::vector<uint8_t>> mTcpOutBuffer;
std::deque<std::shared_ptr<std::vector<uint8_t>>> mTcpOutBufferQueue;
+ bool packet_received_since_last_check_ = false;
+ std::function<void()> on_participant_disconnected_ = []{}; // do nothing by default
+
+ void ScheduleTimeOutCheck();
+ bool CheckParticipantTimeOut();
void onReceive();
void onDTLSReceive(const uint8_t *data, size_t size);
diff --git a/host/frontend/gcastv2/webrtc/include/webrtc/ServerState.h b/host/frontend/gcastv2/webrtc/include/webrtc/ServerState.h
index b98a3f4..fbda93a 100644
--- a/host/frontend/gcastv2/webrtc/include/webrtc/ServerState.h
+++ b/host/frontend/gcastv2/webrtc/include/webrtc/ServerState.h
@@ -50,18 +50,21 @@
VideoFormat videoFormat() const { return mVideoFormat; }
- size_t acquireHandlerId();
- void releaseHandlerId(size_t id);
+ std::shared_ptr<RunLoop> run_loop() { return mRunLoop; }
+ std::string public_ip() const { return mPublicIp; }
+ void SetPublicIp(const std::string& public_ip) { mPublicIp = public_ip; }
-private:
+ private:
using StreamingSource = android::StreamingSource;
std::shared_ptr<RunLoop> mRunLoop;
VideoFormat mVideoFormat;
- std::weak_ptr<Packetizer> mVideoPacketizer;
- std::weak_ptr<Packetizer> mAudioPacketizer;
+ std::mutex mPacketizerLock;
+
+ std::shared_ptr<Packetizer> mVideoPacketizer;
+ std::shared_ptr<Packetizer> mAudioPacketizer;
std::shared_ptr<StreamingSource> mFrameBufferSource;
@@ -73,7 +76,7 @@
std::shared_ptr<TouchSink> mTouchSink;
std::shared_ptr<KeyboardSink> mKeyboardSink;
- std::set<size_t> mAllocatedHandlerIds;
+ std::string mPublicIp;
void MonitorScreenConnector();
};
diff --git a/host/frontend/gcastv2/webrtc/include/webrtc/client_handler.h b/host/frontend/gcastv2/webrtc/include/webrtc/client_handler.h
new file mode 100644
index 0000000..5a13da8
--- /dev/null
+++ b/host/frontend/gcastv2/webrtc/include/webrtc/client_handler.h
@@ -0,0 +1,115 @@
+/*
+ * 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 <webrtc/AdbHandler.h>
+#include <webrtc/RTPSession.h>
+#include <webrtc/RTPSocketHandler.h>
+#include <webrtc/SDP.h>
+#include <webrtc/ServerState.h>
+
+#include <https/RunLoop.h>
+#include <https/WebSocketHandler.h>
+#include <source/KeyboardSink.h>
+#include <source/TouchSink.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <sstream>
+#include <string>
+#include <vector>
+
+struct ClientHandler : public std::enable_shared_from_this<ClientHandler> {
+ explicit ClientHandler(std::shared_ptr<ServerState> serverState,
+ std::function<void(const Json::Value&)> send_client_cb);
+
+ void HandleMessage(const Json::Value& client_message);
+
+ void OnConnectionClosed(std::function<void()> cb) {
+ on_connection_closed_cb_ = cb;
+ }
+
+ private:
+ enum OptionBits : uint32_t {
+ disableAudio = 1,
+ bundleTracks = 2,
+ enableData = 4,
+ useSingleCertificateForAllTracks = 8,
+ useTCP = 16,
+ };
+
+ using TouchSink = android::TouchSink;
+ using KeyboardSink = android::KeyboardSink;
+
+ std::shared_ptr<RunLoop> mRunLoop;
+ std::shared_ptr<ServerState> mServerState;
+ uint32_t mOptions;
+ std::function<void(const Json::Value&)> sendToClient_;
+
+ // Vector has the same ordering as the media entries in the SDP, i.e.
+ // vector index is "mlineIndex". (unless we are bundling, in which case
+ // there is only a single session).
+ std::vector<std::shared_ptr<RTPSession>> mSessions;
+
+ SDP mOfferedSDP;
+ std::vector<std::shared_ptr<RTPSocketHandler>> mRTPs;
+
+ std::shared_ptr<TouchSink> mTouchSink;
+ std::shared_ptr<KeyboardSink> mKeyboardSink;
+
+ std::pair<std::shared_ptr<X509>, std::shared_ptr<EVP_PKEY>>
+ mCertificateAndKey;
+
+ std::function<void()> on_connection_closed_cb_ = []{};
+ std::shared_ptr<AdbHandler> adb_handler_;
+
+ std::shared_ptr<AdbHandler> adb_handler();
+
+ void LogAndReplyError(const std::string& error_msg) const ;
+
+ std::string BuildOffer();
+
+ // Pass -1 for mlineIndex to access the "general" section.
+ std::optional<std::string> getSDPValue(
+ ssize_t mlineIndex, std::string_view key,
+ bool fallthroughToGeneralSection) const;
+
+ std::string getRemotePassword(size_t mlineIndex) const;
+ std::string getRemoteUFrag(size_t mlineIndex) const;
+ std::string getRemoteFingerprint(size_t mlineIndex) const;
+
+ bool GatherAndSendCandidate(int32_t mid);
+
+ static std::pair<std::shared_ptr<X509>, std::shared_ptr<EVP_PKEY>>
+ CreateDTLSCertificateAndKey();
+
+ std::pair<std::string, std::string> createUniqueUFragAndPassword();
+
+ void parseOptions(const Json::Value &options);
+ size_t countTracks() const;
+
+ void prepareSessions();
+
+ void emitTrackIceOptionsAndFingerprint(std::stringstream &ss,
+ size_t mlineIndex) const;
+
+ // Returns -1 on error.
+ ssize_t mlineIndexForMid(int32_t mid) const;
+
+ static void CreateRandomIceCharSequence(char *dst, size_t size);
+};
diff --git a/host/frontend/gcastv2/webrtc/include/webrtc/sig_server_handler.h b/host/frontend/gcastv2/webrtc/include/webrtc/sig_server_handler.h
new file mode 100644
index 0000000..14cef3e
--- /dev/null
+++ b/host/frontend/gcastv2/webrtc/include/webrtc/sig_server_handler.h
@@ -0,0 +1,47 @@
+//
+// 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
+//
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "webrtc/ServerState.h"
+#include "webrtc/client_handler.h"
+#include "webrtc/ws_connection.h"
+
+class SigServerHandler
+ : public WsConnectionObserver,
+ public std::enable_shared_from_this<WsConnectionObserver> {
+ public:
+ SigServerHandler(const std::string& device_id,
+ std::shared_ptr<ServerState> server_state);
+ ~SigServerHandler() override = default;
+
+ void OnOpen() override;
+ void OnClose() override;
+ void OnError(const std::string& error) override;
+ void OnReceive(const uint8_t* msg, size_t length, bool is_binary) override;
+
+ void Connect(const std::string& server_addr, int server_port,
+ const std::string& server_path,
+ WsConnection::Security security);
+
+ private:
+ std::shared_ptr<ServerState> server_state_;
+ std::shared_ptr<WsConnection> server_connection_;
+ std::map<int, std::shared_ptr<ClientHandler>> clients_;
+ std::string device_id_;
+};
diff --git a/host/frontend/gcastv2/webrtc/include/webrtc/ws_connection.h b/host/frontend/gcastv2/webrtc/include/webrtc/ws_connection.h
new file mode 100644
index 0000000..ca81dd0
--- /dev/null
+++ b/host/frontend/gcastv2/webrtc/include/webrtc/ws_connection.h
@@ -0,0 +1,73 @@
+//
+// 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
+//
+
+#pragma once
+
+#include <string.h>
+
+#include <deque>
+#include <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "libwebsockets.h"
+
+class WsConnectionObserver {
+ public:
+ virtual ~WsConnectionObserver() = default;
+ virtual void OnOpen() = 0;
+ virtual void OnClose() = 0;
+ virtual void OnError(const std::string& error) = 0;
+ virtual void OnReceive(const uint8_t* msg, size_t length, bool is_binary) = 0;
+};
+
+class WsConnection {
+ public:
+ enum class Security {
+ kInsecure,
+ kAllowSelfSigned,
+ kStrict,
+ };
+
+ static std::shared_ptr<WsConnection> Create();
+
+ virtual ~WsConnection() = default;
+
+ virtual void Connect() = 0;
+
+ virtual bool Send(const uint8_t* data, size_t len, bool binary = false) = 0;
+
+ protected:
+ WsConnection() = default;
+};
+
+class WsConnectionContext {
+ public:
+ static std::shared_ptr<WsConnectionContext> Create();
+
+ virtual ~WsConnectionContext() = default;
+
+ virtual std::shared_ptr<WsConnection> CreateConnection(
+ int port, const std::string& addr, const std::string& path,
+ WsConnection::Security secure,
+ std::weak_ptr<WsConnectionObserver> observer) = 0;
+
+ protected:
+ WsConnectionContext() = default;
+};
diff --git a/host/frontend/gcastv2/webrtc/sig_server_handler.cpp b/host/frontend/gcastv2/webrtc/sig_server_handler.cpp
new file mode 100644
index 0000000..5f2d1c4
--- /dev/null
+++ b/host/frontend/gcastv2/webrtc/sig_server_handler.cpp
@@ -0,0 +1,265 @@
+//
+// 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
+//
+
+#include "host/frontend/gcastv2/webrtc/include/webrtc/sig_server_handler.h"
+
+#include <android-base/strings.h>
+#include <gflags/gflags.h>
+#include <json/json.h>
+
+#include <webrtc/STUNClient.h>
+#include <webrtc/STUNMessage.h>
+#include <webrtc/ServerState.h>
+#include "Utils.h"
+
+#include "host/frontend/gcastv2/signaling_server/constants/signaling_constants.h"
+
+DECLARE_string(public_ip);
+
+namespace {
+
+constexpr auto kStreamIdField = "stream_id";
+constexpr auto kXResField = "x_res";
+constexpr auto kYResField = "y_res";
+constexpr auto kDpiField = "dpi";
+constexpr auto kIsTouchField = "is_touch";
+constexpr auto kDisplaysField = "displays";
+
+std::string FigureOutPublicIp(const std::string &stun_server) {
+ if (!FLAGS_public_ip.empty() && FLAGS_public_ip != "0.0.0.0") {
+ return FLAGS_public_ip;
+ }
+
+ // NOTE: We only contact the external STUN server once upon startup
+ // to determine our own public IP.
+ // This only works if NAT does not remap ports, i.e. a local port 15550
+ // is visible to the outside world on port 15550 as well.
+ // If this condition is not met, this code will have to be modified
+ // and a STUN request made for each locally bound socket before
+ // fulfilling a "MyWebSocketHandler::getCandidate" ICE request.
+
+ const addrinfo kHints = {
+ AI_ADDRCONFIG,
+ PF_INET,
+ SOCK_DGRAM,
+ IPPROTO_UDP,
+ 0, // ai_addrlen
+ nullptr, // ai_addr
+ nullptr, // ai_canonname
+ nullptr // ai_next
+ };
+
+ auto pieces = SplitString(stun_server, ':');
+ CHECK_EQ(pieces.size(), 2u);
+
+ addrinfo *infos;
+ CHECK(!getaddrinfo(pieces[0].c_str(), pieces[1].c_str(), &kHints, &infos));
+
+ sockaddr_storage stunAddr;
+ memcpy(&stunAddr, infos->ai_addr, infos->ai_addrlen);
+
+ freeaddrinfo(infos);
+ infos = nullptr;
+
+ CHECK_EQ(stunAddr.ss_family, AF_INET);
+
+ std::mutex lock;
+ std::condition_variable cond;
+ bool done = false;
+
+ auto runLoop = std::make_shared<RunLoop>("STUN");
+ std::string public_ip;
+
+ auto stunClient = std::make_shared<STUNClient>(
+ runLoop, reinterpret_cast<const sockaddr_in &>(stunAddr),
+ [&lock, &cond, &done, &public_ip](int result,
+ const std::string &myPublicIp) {
+ CHECK(!result);
+ LOG(INFO) << "STUN-discovered public IP: " << myPublicIp;
+
+ public_ip = myPublicIp;
+
+ std::lock_guard autoLock(lock);
+ done = true;
+ cond.notify_all();
+ });
+
+ stunClient->run();
+
+ std::unique_lock autoLock(lock);
+ while (!done) {
+ cond.wait(autoLock);
+ }
+ return public_ip;
+}
+
+std::string StunServerFromConfig(const Json::Value &server_config) {
+ if (!server_config.isMember(cvd::webrtc_signaling::kServersField) ||
+ !server_config[cvd::webrtc_signaling::kServersField].isArray()) {
+ return "";
+ }
+ auto ice_servers = server_config[cvd::webrtc_signaling::kServersField];
+ for (Json::ArrayIndex i = 0; i < ice_servers.size(); ++i) {
+ if (!ice_servers[i].isMember(cvd::webrtc_signaling::kUrlsField)) {
+ LOG(WARNING) << "Ice server received without a urls field";
+ continue;
+ }
+ auto url = ice_servers[i][cvd::webrtc_signaling::kUrlsField];
+ if (url.isArray()) {
+ if (url.size() == 0) {
+ LOG(WARNING) << "Ice server received with empty urls field";
+ continue;
+ }
+ url = url[0];
+ }
+ if (!url.isString()) {
+ LOG(WARNING) << "Ice server with non-string url";
+ continue;
+ }
+ auto url_str = url.asString();
+ if (::android::base::StartsWith(url_str, "stun:")) {
+ return url_str.substr(std::string("stun:").size());
+ }
+ }
+ return "";
+}
+
+void SendJson(std::shared_ptr<WsConnection> ws_conn, const Json::Value &data) {
+ Json::FastWriter json_writer;
+ auto data_str = json_writer.write(data);
+ ws_conn->Send(reinterpret_cast<const uint8_t *>(data_str.c_str()),
+ data_str.size());
+}
+
+bool ParseMessage(const uint8_t *data, size_t length, Json::Value *msg_out) {
+ Json::Reader json_reader;
+ auto str = reinterpret_cast<const char *>(data);
+ return json_reader.parse(str, str + length, *msg_out) >= 0;
+}
+
+} // namespace
+
+SigServerHandler::SigServerHandler(const std::string &device_id,
+ std::shared_ptr<ServerState> server_state)
+ : server_state_(server_state), device_id_(device_id) {}
+
+void SigServerHandler::Connect(const std::string &server_addr, int server_port,
+ const std::string &server_path,
+ WsConnection::Security security) {
+ // This can be a local variable since the connection object will keep a
+ // reference to it.
+ auto ws_context = WsConnectionContext::Create();
+ server_connection_ = ws_context->CreateConnection(
+ server_port, server_addr, server_path, security, weak_from_this());
+
+ CHECK(server_connection_) << "Unable to create websocket connection object";
+
+ server_connection_->Connect();
+}
+
+void SigServerHandler::OnOpen() {
+ auto config = vsoc::CuttlefishConfig::Get();
+ Json::Value register_obj;
+ register_obj[cvd::webrtc_signaling::kTypeField] =
+ cvd::webrtc_signaling::kRegisterType;
+ register_obj[cvd::webrtc_signaling::kDeviceIdField] =
+ device_id_.empty() ? config->ForDefaultInstance().instance_name()
+ : device_id_;
+ Json::Value device_info;
+ Json::Value displays(Json::ValueType::arrayValue);
+ Json::Value main_display;
+
+ main_display[kStreamIdField] = "display_0";
+ main_display[kXResField] = config->x_res();
+ main_display[kYResField] = config->y_res();
+ main_display[kDpiField] = config->dpi();
+ main_display[kIsTouchField] = true;
+
+ displays.append(main_display);
+ device_info[kDisplaysField] = displays;
+ register_obj[cvd::webrtc_signaling::kDeviceInfoField] = device_info;
+ SendJson(server_connection_, register_obj);
+}
+
+void SigServerHandler::OnClose() {
+ LOG(WARNING) << "Websocket closed unexpectedly";
+}
+
+void SigServerHandler::OnError(const std::string &error) {
+ LOG(FATAL) << "Error detected on server connection: " << error;
+}
+
+void SigServerHandler::OnReceive(const uint8_t *data, size_t length,
+ bool is_binary) {
+ Json::Value server_message;
+ if (is_binary || !ParseMessage(data, length, &server_message)) {
+ LOG(ERROR) << "Received invalid JSON from server: '"
+ << (is_binary ? std::string("(binary_data)")
+ : std::string(data, data + length))
+ << "'";
+ return;
+ }
+ if (!server_message.isMember(cvd::webrtc_signaling::kTypeField) ||
+ !server_message[cvd::webrtc_signaling::kTypeField].isString()) {
+ LOG(ERROR) << "No message_type field from server";
+ return;
+ }
+ auto type = server_message[cvd::webrtc_signaling::kTypeField].asString();
+ if (type == cvd::webrtc_signaling::kConfigType) {
+ auto stun_server = StunServerFromConfig(server_message);
+ auto public_ip =
+ stun_server.empty() ? FLAGS_public_ip : FigureOutPublicIp(stun_server);
+ server_state_->SetPublicIp(public_ip);
+ } else if (type == cvd::webrtc_signaling::kClientMessageType) {
+ if (!server_message.isMember(cvd::webrtc_signaling::kClientIdField) ||
+ !server_message[cvd::webrtc_signaling::kClientIdField].isInt()) {
+ LOG(ERROR) << "Client message received without valid client id";
+ return;
+ }
+ auto client_id =
+ server_message[cvd::webrtc_signaling::kClientIdField].asInt();
+ if (!server_message.isMember(cvd::webrtc_signaling::kPayloadField)) {
+ LOG(ERROR) << "Received empty client message";
+ return;
+ }
+ auto client_message = server_message[cvd::webrtc_signaling::kPayloadField];
+ if (clients_.count(client_id) == 0) {
+ clients_[client_id].reset(new ClientHandler(
+ server_state_, [this, client_id](const Json::Value &msg) {
+ Json::Value wrapper;
+ wrapper[cvd::webrtc_signaling::kPayloadField] = msg;
+ wrapper[cvd::webrtc_signaling::kTypeField] =
+ cvd::webrtc_signaling::kForwardType;
+ wrapper[cvd::webrtc_signaling::kClientIdField] = client_id;
+ // This is safe to call from the webrtc runloop because
+ // WsConnection is thread safe
+ SendJson(server_connection_, wrapper);
+ }));
+ clients_[client_id]->OnConnectionClosed([this, client_id]{
+ clients_.erase(client_id);
+ });
+ }
+
+ auto client_handler = clients_[client_id];
+
+ // Client handler operations need to happen in its own runloop
+ server_state_->run_loop()->post([client_handler, client_message] {
+ client_handler->HandleMessage(client_message);
+ });
+ } else {
+ LOG(ERROR) << "Unknown message type: " << type;
+ return;
+ }
+}
diff --git a/host/frontend/gcastv2/webrtc/webRTC.cpp b/host/frontend/gcastv2/webrtc/webRTC.cpp
index 427f1a3..e17309d 100644
--- a/host/frontend/gcastv2/webrtc/webRTC.cpp
+++ b/host/frontend/gcastv2/webrtc/webRTC.cpp
@@ -14,21 +14,24 @@
* limitations under the License.
*/
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <host/libs/config/cuttlefish_config.h>
+
#include "Utils.h"
-#include <webrtc/AdbWebSocketHandler.h>
+#include <webrtc/AdbHandler.h>
#include <webrtc/DTLS.h>
-#include <webrtc/MyWebSocketHandler.h>
#include <webrtc/RTPSocketHandler.h>
#include <webrtc/ServerState.h>
-#include <webrtc/STUNClient.h>
-#include <webrtc/STUNMessage.h>
+#include <webrtc/sig_server_handler.h>
#include <https/HTTPServer.h>
#include <https/PlainSocket.h>
#include <https/RunLoop.h>
-#include <https/SafeCallbackable.h>
#include <https/SSLSocket.h>
+#include <https/SafeCallbackable.h>
#include <https/Support.h>
#include <iostream>
@@ -38,159 +41,48 @@
#include <gflags/gflags.h>
-DEFINE_int32(http_server_port, 8443, "The port for the http server.");
-DEFINE_bool(use_secure_http, true, "Whether to use HTTPS or HTTP.");
-DEFINE_string(
- public_ip,
- "0.0.0.0",
- "Public IPv4 address of your server, a.b.c.d format");
-DEFINE_string(
- assets_dir,
- "webrtc",
- "Directory with location of webpage assets.");
-DEFINE_string(
- certs_dir,
- "webrtc/certs",
- "Directory to certificates.");
+DEFINE_string(public_ip, "0.0.0.0", "Public IPv4 address, a.b.c.d format");
DEFINE_int32(touch_fd, -1, "An fd to listen on for touch connections.");
DEFINE_int32(keyboard_fd, -1, "An fd to listen on for keyboard connections.");
DEFINE_int32(frame_server_fd, -1, "An fd to listen on for frame updates");
-DEFINE_bool(write_virtio_input, false, "Whether to send input events in virtio format.");
+DEFINE_bool(write_virtio_input, false,
+ "Whether to send input events in virtio format.");
DEFINE_string(adb, "", "Interface:port of local adb service.");
-DEFINE_string(
- stun_server,
- "stun.l.google.com:19302",
- "host:port of STUN server to use for public address resolution");
-
int main(int argc, char **argv) {
- ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+ ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
- SSLSocket::Init();
- DTLS::Init();
+ SSLSocket::Init();
+ DTLS::Init();
- if (FLAGS_public_ip.empty() || FLAGS_public_ip == "0.0.0.0") {
- // NOTE: We only contact the external STUN server once upon startup
- // to determine our own public IP.
- // This only works if NAT does not remap ports, i.e. a local port 15550
- // is visible to the outside world on port 15550 as well.
- // If this condition is not met, this code will have to be modified
- // and a STUN request made for each locally bound socket before
- // fulfilling a "MyWebSocketHandler::getCandidate" ICE request.
+ auto config = vsoc::CuttlefishConfig::Get();
- const addrinfo kHints = {
- AI_ADDRCONFIG,
- PF_INET,
- SOCK_DGRAM,
- IPPROTO_UDP,
- 0, // ai_addrlen
- nullptr, // ai_addr
- nullptr, // ai_canonname
- nullptr // ai_next
- };
+ auto sig_server_addr = config->sig_server_address();
+ auto sig_server_port = config->sig_server_port();
+ auto sig_server_path = config->sig_server_path();
+ auto sig_server_strict = config->sig_server_strict();
+ auto device_id = config->ForDefaultInstance().webrtc_device_id();
- auto pieces = SplitString(FLAGS_stun_server, ':');
- CHECK_EQ(pieces.size(), 2u);
+ auto runLoop = RunLoop::main();
- addrinfo *infos;
- CHECK(!getaddrinfo(pieces[0].c_str(), pieces[1].c_str(), &kHints, &infos));
+ auto state =
+ std::make_shared<ServerState>(runLoop, ServerState::VideoFormat::VP8);
- sockaddr_storage stunAddr;
- memcpy(&stunAddr, infos->ai_addr, infos->ai_addrlen);
+ auto security = WsConnection::Security::kAllowSelfSigned;
+ if (sig_server_strict) {
+ security = WsConnection::Security::kStrict;
+ }
- freeaddrinfo(infos);
- infos = nullptr;
+ auto sig_server_handler =
+ std::make_shared<SigServerHandler>(device_id, state);
- CHECK_EQ(stunAddr.ss_family, AF_INET);
+ sig_server_handler->Connect(sig_server_addr, sig_server_port, sig_server_path,
+ security);
- std::mutex lock;
- std::condition_variable cond;
- bool done = false;
+ runLoop->run();
- auto runLoop = std::make_shared<RunLoop>("STUN");
-
- auto stunClient = std::make_shared<STUNClient>(
- runLoop,
- reinterpret_cast<const sockaddr_in &>(stunAddr),
- [&lock, &cond, &done](int result, const std::string &myPublicIp) {
- CHECK(!result);
- LOG(INFO)
- << "STUN-discovered public IP: " << myPublicIp;
-
- FLAGS_public_ip = myPublicIp;
-
- std::lock_guard autoLock(lock);
- done = true;
- cond.notify_all();
- });
-
- stunClient->run();
-
- std::unique_lock autoLock(lock);
- while (!done) {
- cond.wait(autoLock);
- }
- }
-
- auto runLoop = RunLoop::main();
-
- auto state = std::make_shared<ServerState>(
- runLoop, ServerState::VideoFormat::VP8);
-
- auto port = FLAGS_http_server_port;
-
- auto httpd = std::make_shared<HTTPServer>(
- runLoop,
- "0.0.0.0",
- port,
- FLAGS_use_secure_http
- ? ServerSocket::TransportType::TLS
- : ServerSocket::TransportType::TCP,
- FLAGS_certs_dir + "/server.crt",
- FLAGS_certs_dir + "/server.key");
-
- const std::string index_html = FLAGS_assets_dir + "/index.html";
- const std::string logcat_js = FLAGS_assets_dir + "/js/logcat.js";
- const std::string app_js = FLAGS_assets_dir + "/js/app.js";
- const std::string viewpane_js = FLAGS_assets_dir + "/js/viewpane.js";
- const std::string cf_webrtc_js = FLAGS_assets_dir + "/js/cf_webrtc.js";
- const std::string style_css = FLAGS_assets_dir + "/style.css";
-
- httpd->addStaticFile("/index.html", index_html.c_str());
- httpd->addStaticFile("/js/logcat.js", logcat_js.c_str());
- httpd->addStaticFile("/js/app.js", app_js.c_str());
- httpd->addStaticFile("/js/viewpane.js", viewpane_js.c_str());
- httpd->addStaticFile("/js/cf_webrtc.js", cf_webrtc_js.c_str());
- httpd->addStaticFile("/style.css", style_css.c_str());
-
- httpd->addWebSocketHandlerFactory(
- "/control",
- [runLoop, state]{
- auto id = state->acquireHandlerId();
-
- auto handler =
- std::make_shared<MyWebSocketHandler>(runLoop, state, id);
-
- return std::make_pair(0 /* OK */, handler);
- });
-
- if (!FLAGS_adb.empty()) {
- httpd->addWebSocketHandlerFactory(
- "/control_adb",
- [runLoop]{
- auto handler = std::make_shared<AdbWebSocketHandler>(
- runLoop, FLAGS_adb);
-
- handler->run();
-
- return std::make_pair(0 /* OK */, handler);
- });
- }
-
- httpd->run();
- runLoop->run();
-
- return 0;
+ return 0;
}
diff --git a/host/frontend/gcastv2/webrtc/ws_connection.cpp b/host/frontend/gcastv2/webrtc/ws_connection.cpp
new file mode 100644
index 0000000..3d3977a
--- /dev/null
+++ b/host/frontend/gcastv2/webrtc/ws_connection.cpp
@@ -0,0 +1,403 @@
+//
+// 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
+//
+
+#include "webrtc/ws_connection.h"
+
+#include "android-base/logging.h"
+#include "libwebsockets.h"
+
+class WsConnectionContextImpl;
+
+class WsConnectionImpl : public WsConnection,
+ public std::enable_shared_from_this<WsConnectionImpl> {
+ public:
+ struct CreateConnectionSul {
+ lws_sorted_usec_list_t sul = {};
+ std::weak_ptr<WsConnectionImpl> weak_this;
+ };
+
+ WsConnectionImpl(int port, const std::string& addr, const std::string& path,
+ Security secure,
+ std::weak_ptr<WsConnectionObserver> observer,
+ std::shared_ptr<WsConnectionContextImpl> context);
+
+ ~WsConnectionImpl() override;
+
+ void Connect() override;
+ void ConnectInner();
+
+ bool Send(const uint8_t* data, size_t len, bool binary = false) override;
+
+ void OnError(const std::string& error);
+ void OnReceive(const uint8_t* data, size_t len, bool is_binary);
+ void OnOpen();
+ void OnClose();
+ void OnWriteable();
+
+ private:
+ struct WsBuffer {
+ WsBuffer() = default;
+ WsBuffer(const uint8_t* data, size_t len, bool binary)
+ : buffer_(LWS_PRE + len), is_binary_(binary) {
+ memcpy(&buffer_[LWS_PRE], data, len);
+ }
+
+ uint8_t* data() { return &buffer_[LWS_PRE]; }
+ bool is_binary() const { return is_binary_; }
+ size_t size() const { return buffer_.size() - LWS_PRE; }
+
+ private:
+ std::vector<uint8_t> buffer_;
+ bool is_binary_;
+ };
+
+ CreateConnectionSul extended_sul_;
+ struct lws* wsi_;
+ const int port_;
+ const std::string addr_;
+ const std::string path_;
+ const Security security_;
+
+ std::weak_ptr<WsConnectionObserver> observer_;
+
+ // each element contains the data to be sent and whether it's binary or not
+ std::deque<WsBuffer> write_queue_;
+ std::mutex write_queue_mutex_;
+ // The connection object should not outlive the context object. This reference
+ // guarantees it.
+ std::shared_ptr<WsConnectionContextImpl> context_;
+};
+
+class WsConnectionContextImpl
+ : public WsConnectionContext,
+ public std::enable_shared_from_this<WsConnectionContextImpl> {
+ public:
+ WsConnectionContextImpl(struct lws_context* lws_ctx);
+ ~WsConnectionContextImpl() override;
+
+ std::shared_ptr<WsConnection> CreateConnection(
+ int port, const std::string& addr, const std::string& path,
+ WsConnection::Security secure,
+ std::weak_ptr<WsConnectionObserver> observer) override;
+
+ void RememberConnection(void*, std::weak_ptr<WsConnectionImpl>);
+ void ForgetConnection(void*);
+ std::shared_ptr<WsConnectionImpl> GetConnection(void*);
+
+ struct lws_context* lws_context() {
+ return lws_context_;
+ }
+
+ private:
+ void Start();
+
+ std::map<void*, std::weak_ptr<WsConnectionImpl>> weak_by_ptr_;
+ std::mutex map_mutex_;
+ struct lws_context* lws_context_;
+ std::thread message_loop_;
+};
+
+int LwsCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user,
+ void* in, size_t len);
+void CreateConnectionCallback(lws_sorted_usec_list_t* sul);
+
+namespace {
+
+constexpr char kProtocolName[] = "lws-websocket-protocol";
+constexpr int kBufferSize = 65536;
+
+const uint32_t backoff_ms[] = {1000, 2000, 3000, 4000, 5000};
+
+const lws_retry_bo_t kRetry = {
+ .retry_ms_table = backoff_ms,
+ .retry_ms_table_count = LWS_ARRAY_SIZE(backoff_ms),
+ .conceal_count = LWS_ARRAY_SIZE(backoff_ms),
+
+ .secs_since_valid_ping = 3, /* force PINGs after secs idle */
+ .secs_since_valid_hangup = 10, /* hangup after secs idle */
+
+ .jitter_percent = 20,
+};
+
+const struct lws_protocols kProtocols[2] = {
+ {kProtocolName, LwsCallback, 0, kBufferSize, 0, NULL, 0},
+ {NULL, NULL, 0, 0, 0, NULL, 0}};
+
+} // namespace
+
+std::shared_ptr<WsConnectionContext> WsConnectionContext::Create() {
+ struct lws_context_creation_info context_info = {};
+ context_info.port = CONTEXT_PORT_NO_LISTEN;
+ context_info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+ context_info.protocols = kProtocols;
+ struct lws_context* lws_ctx = lws_create_context(&context_info);
+ if (!lws_ctx) {
+ return nullptr;
+ }
+ auto ret = std::shared_ptr<WsConnectionContext>(
+ new WsConnectionContextImpl(lws_ctx));
+ return ret;
+}
+
+WsConnectionContextImpl::WsConnectionContextImpl(struct lws_context* lws_ctx)
+ : lws_context_(lws_ctx) {
+ Start();
+}
+
+WsConnectionContextImpl::~WsConnectionContextImpl() {
+ lws_context_destroy(lws_context_);
+ if (message_loop_.joinable()) message_loop_.join();
+}
+
+void WsConnectionContextImpl::Start() {
+ message_loop_ = std::thread([this]() {
+ for (;;) {
+ if (lws_service(lws_context_, 0) < 0) {
+ break;
+ }
+ }
+ });
+}
+
+std::shared_ptr<WsConnection> WsConnectionContextImpl::CreateConnection(
+ int port, const std::string& addr, const std::string& path,
+ WsConnection::Security security,
+ std::weak_ptr<WsConnectionObserver> observer) {
+ std::shared_ptr<WsConnection> ret(new WsConnectionImpl(
+ port, addr, path, security, observer, shared_from_this()));
+ return ret;
+}
+
+std::shared_ptr<WsConnectionImpl> WsConnectionContextImpl::GetConnection(
+ void* raw) {
+ std::shared_ptr<WsConnectionImpl> connection;
+ {
+ std::lock_guard<std::mutex> lock(map_mutex_);
+ if (weak_by_ptr_.count(raw) == 0) {
+ return nullptr;
+ }
+ connection = weak_by_ptr_[raw].lock();
+ if (!connection) {
+ weak_by_ptr_.erase(raw);
+ }
+ }
+ return connection;
+}
+
+void WsConnectionContextImpl::RememberConnection(
+ void* raw, std::weak_ptr<WsConnectionImpl> conn) {
+ std::lock_guard<std::mutex> lock(map_mutex_);
+ weak_by_ptr_.emplace(
+ std::pair<void*, std::weak_ptr<WsConnectionImpl>>(raw, conn));
+}
+
+void WsConnectionContextImpl::ForgetConnection(void* raw) {
+ std::lock_guard<std::mutex> lock(map_mutex_);
+ weak_by_ptr_.erase(raw);
+}
+
+WsConnectionImpl::WsConnectionImpl(
+ int port, const std::string& addr, const std::string& path,
+ Security security, std::weak_ptr<WsConnectionObserver> observer,
+ std::shared_ptr<WsConnectionContextImpl> context)
+ : port_(port),
+ addr_(addr),
+ path_(path),
+ security_(security),
+ observer_(observer),
+ context_(context) {}
+
+WsConnectionImpl::~WsConnectionImpl() {
+ context_->ForgetConnection(this);
+ // This will cause the callback to be called which will drop the connection
+ // after seeing the context doesn't remember this object
+ lws_callback_on_writable(wsi_);
+}
+
+void WsConnectionImpl::Connect() {
+ memset(&extended_sul_.sul, 0, sizeof(extended_sul_.sul));
+ extended_sul_.weak_this = weak_from_this();
+ lws_sul_schedule(context_->lws_context(), 0, &extended_sul_.sul,
+ CreateConnectionCallback, 1);
+}
+
+void WsConnectionImpl::OnError(const std::string& error) {
+ auto observer = observer_.lock();
+ if (observer) {
+ observer->OnError(error);
+ }
+}
+void WsConnectionImpl::OnReceive(const uint8_t* data, size_t len,
+ bool is_binary) {
+ auto observer = observer_.lock();
+ if (observer) {
+ observer->OnReceive(data, len, is_binary);
+ }
+}
+void WsConnectionImpl::OnOpen() {
+ auto observer = observer_.lock();
+ if (observer) {
+ observer->OnOpen();
+ }
+}
+void WsConnectionImpl::OnClose() {
+ auto observer = observer_.lock();
+ if (observer) {
+ observer->OnClose();
+ }
+}
+
+void WsConnectionImpl::OnWriteable() {
+ WsBuffer buffer;
+ {
+ std::lock_guard<std::mutex> lock(write_queue_mutex_);
+ if (write_queue_.size() == 0) {
+ return;
+ }
+ buffer = std::move(write_queue_.front());
+ write_queue_.pop_front();
+ }
+ auto flags = lws_write_ws_flags(
+ buffer.is_binary() ? LWS_WRITE_BINARY : LWS_WRITE_TEXT, true, true);
+ auto res = lws_write(wsi_, buffer.data(), buffer.size(),
+ (enum lws_write_protocol)flags);
+ if (res != buffer.size()) {
+ LOG(WARNING) << "Unable to send the entire message!";
+ }
+}
+
+bool WsConnectionImpl::Send(const uint8_t* data, size_t len, bool binary) {
+ if (!wsi_) {
+ LOG(WARNING) << "Send called on an uninitialized connection!!";
+ return false;
+ }
+ WsBuffer buffer(data, len, binary);
+ {
+ std::lock_guard<std::mutex> lock(write_queue_mutex_);
+ write_queue_.emplace_back(std::move(buffer));
+ }
+
+ lws_callback_on_writable(wsi_);
+ return true;
+}
+
+int LwsCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user,
+ void* in, size_t len) {
+ constexpr int DROP = -1;
+ constexpr int OK = 0;
+
+ // For some values of `reason`, `user` doesn't point to the value provided
+ // when the connection was created. This function object should be used with
+ // care.
+ auto with_connection =
+ [wsi, user](std::function<void(std::shared_ptr<WsConnectionImpl>)> cb) {
+ auto context = reinterpret_cast<WsConnectionContextImpl*>(user);
+ auto connection = context->GetConnection(wsi);
+ if (!connection) {
+ return DROP;
+ }
+ cb(connection);
+ return OK;
+ };
+
+ switch (reason) {
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ return with_connection(
+ [in](std::shared_ptr<WsConnectionImpl> connection) {
+ connection->OnError(in ? (char*)in : "(null)");
+ });
+
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ return with_connection(
+ [in, len, wsi](std::shared_ptr<WsConnectionImpl> connection) {
+ connection->OnReceive((const uint8_t*)in, len,
+ lws_frame_is_binary(wsi));
+ });
+
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+ return with_connection([](std::shared_ptr<WsConnectionImpl> connection) {
+ connection->OnOpen();
+ });
+
+ case LWS_CALLBACK_CLIENT_CLOSED:
+ return with_connection([](std::shared_ptr<WsConnectionImpl> connection) {
+ connection->OnClose();
+ });
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+ return with_connection([](std::shared_ptr<WsConnectionImpl> connection) {
+ connection->OnWriteable();
+ });
+
+ default:
+ LOG(VERBOSE) << "Unhandled value: " << reason;
+ return lws_callback_http_dummy(wsi, reason, user, in, len);
+ }
+}
+
+void CreateConnectionCallback(lws_sorted_usec_list_t* sul) {
+ std::shared_ptr<WsConnectionImpl> connection =
+ reinterpret_cast<WsConnectionImpl::CreateConnectionSul*>(sul)
+ ->weak_this.lock();
+ if (!connection) {
+ LOG(WARNING) << "The object was already destroyed by the time of the first "
+ << "connection attempt. That's unusual.";
+ return;
+ }
+ connection->ConnectInner();
+}
+
+void WsConnectionImpl::ConnectInner() {
+ struct lws_client_connect_info connect_info;
+
+ memset(&connect_info, 0, sizeof(connect_info));
+
+ connect_info.context = context_->lws_context();
+ connect_info.port = port_;
+ connect_info.address = addr_.c_str();
+ connect_info.path = path_.c_str();
+ connect_info.host = connect_info.address;
+ connect_info.origin = connect_info.address;
+ switch (security_) {
+ case Security::kAllowSelfSigned:
+ connect_info.ssl_connection = LCCSCF_ALLOW_SELFSIGNED |
+ LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK |
+ LCCSCF_USE_SSL;
+ break;
+ case Security::kStrict:
+ connect_info.ssl_connection = LCCSCF_USE_SSL;
+ break;
+ case Security::kInsecure:
+ connect_info.ssl_connection = 0;
+ break;
+ }
+ connect_info.protocol = "UNNUSED";
+ connect_info.local_protocol_name = kProtocolName;
+ connect_info.pwsi = &wsi_;
+ connect_info.retry_and_idle_policy = &kRetry;
+ // There is no guarantee the connection object still exists when the callback
+ // is called. Put the context instead as the user data which is guaranteed to
+ // still exist and holds a weak ptr to the connection.
+ connect_info.userdata = context_.get();
+
+ if (lws_client_connect_via_info(&connect_info)) {
+ // wsi_ is not initialized until after the call to
+ // lws_client_connect_via_info(). Luckily, this is guaranteed to run before
+ // the protocol callback is called because it runs in the same loop.
+ context_->RememberConnection(wsi_, weak_from_this());
+ } else {
+ LOG(ERROR) << "Connection failed!";
+ }
+}
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 83428a9..af27987 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -153,6 +153,13 @@
const char* kTombstoneReceiverPort = "tombstone_receiver_port";
const char* kWebRTCCertsDir = "webrtc_certs_dir";
+const char* kSigServerBinary = "webrtc_sig_server_binary";
+const char* kSigServerPort = "webrtc_sig_server_port";
+const char* kSigServerAddress = "webrtc_sig_server_addr";
+const char* kSigServerPath = "webrtc_sig_server_path";
+const char* kSigServerStrict = "webrtc_sig_server_strict";
+const char* kWebrtcDeviceId = "webrtc_device_id";
+const char* kStartSigServer = "webrtc_start_sig_server";
const char* kBootloader = "bootloader";
const char* kUseBootloader = "use_bootloader";
@@ -172,6 +179,9 @@
const char* kFramesServerPort = "frames_server_port";
const char* kTouchServerPort = "touch_server_port";
const char* kKeyboardServerPort = "keyboard_server_port";
+
+const char* kRilDns = "ril_dns";
+const char* kKeymasterVsockPort = "keymaster_vsock_port";
} // namespace
namespace vsoc {
@@ -408,6 +418,10 @@
return cvd::AbsolutePath(PerInstancePath("launcher.log"));
}
+std::string CuttlefishConfig::InstanceSpecific::sdcard_path() const {
+ return cvd::AbsolutePath(PerInstancePath("sdcard.img"));
+}
+
std::string CuttlefishConfig::InstanceSpecific::mobile_bridge_name() const {
return (*Dictionary())[kMobileBridgeName].asString();
}
@@ -633,6 +647,14 @@
(*Dictionary())[kKeyboardServerPort] = keyboard_server_port;
}
+int CuttlefishConfig::InstanceSpecific::keymaster_vsock_port() const {
+ return (*Dictionary())[kKeymasterVsockPort].asInt();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_keymaster_vsock_port(int keymaster_vsock_port) {
+ (*Dictionary())[kKeymasterVsockPort] = keymaster_vsock_port;
+}
+
int CuttlefishConfig::InstanceSpecific::tombstone_receiver_port() const {
return (*Dictionary())[kTombstoneReceiverPort].asInt();
}
@@ -856,6 +878,64 @@
return (*dictionary_)[kWebRTCCertsDir].asString();
}
+void CuttlefishConfig::set_sig_server_binary(const std::string& binary) {
+ SetPath(kSigServerBinary, binary);
+}
+
+std::string CuttlefishConfig::sig_server_binary() const {
+ return (*dictionary_)[kSigServerBinary].asString();
+}
+
+void CuttlefishConfig::set_sig_server_port(int port) {
+ (*dictionary_)[kSigServerPort] = port;
+}
+
+int CuttlefishConfig::sig_server_port() const {
+ return (*dictionary_)[kSigServerPort].asInt();
+}
+
+void CuttlefishConfig::set_sig_server_address(const std::string& addr) {
+ (*dictionary_)[kSigServerAddress] = addr;
+}
+
+std::string CuttlefishConfig::sig_server_address() const {
+ return (*dictionary_)[kSigServerAddress].asString();
+}
+
+void CuttlefishConfig::set_sig_server_path(const std::string& path) {
+ // Don't use SetPath here, it's a URL path not a file system path
+ (*dictionary_)[kSigServerPath] = path;
+}
+
+std::string CuttlefishConfig::sig_server_path() const {
+ return (*dictionary_)[kSigServerPath].asString();
+}
+
+void CuttlefishConfig::set_sig_server_strict(bool strict) {
+ (*dictionary_)[kSigServerStrict] = strict;
+}
+
+bool CuttlefishConfig::sig_server_strict() const {
+ return (*dictionary_)[kSigServerStrict].asBool();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_webrtc_device_id(
+ const std::string& id) {
+ (*Dictionary())[kWebrtcDeviceId] = id;
+}
+
+std::string CuttlefishConfig::InstanceSpecific::webrtc_device_id() const {
+ return (*Dictionary())[kWebrtcDeviceId].asString();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_start_webrtc_signaling_server(bool start) {
+ (*Dictionary())[kStartSigServer] = start;
+}
+
+bool CuttlefishConfig::InstanceSpecific::start_webrtc_sig_server() const {
+ return (*Dictionary())[kStartSigServer].asBool();
+}
+
std::string CuttlefishConfig::InstanceSpecific::touch_socket_path() const {
return PerInstanceInternalPath("touch.sock");
}
@@ -954,6 +1034,13 @@
return cmdline;
}
+void CuttlefishConfig::set_ril_dns(const std::string& ril_dns) {
+ (*dictionary_)[kRilDns] = ril_dns;
+}
+std::string CuttlefishConfig::ril_dns()const {
+ return (*dictionary_)[kRilDns].asString();
+}
+
// Creates the (initially empty) config object and populates it with values from
// the config file if the CUTTLEFISH_CONFIG_FILE env variable is present.
// Returns nullptr if there was an error loading from file
@@ -1081,12 +1168,6 @@
}
int ForCurrentInstance(int base) { return base + GetInstance() - 1; }
-std::string GetDefaultPerInstanceDir() {
- std::ostringstream stream;
- stream << std::getenv("HOME") << "/cuttlefish_runtime";
- return stream.str();
-}
-
int GetDefaultPerInstanceVsockCid() {
constexpr int kFirstGuestCid = 3;
return vsoc::HostSupportsVsock() ? ForCurrentInstance(kFirstGuestCid) : 0;
@@ -1101,8 +1182,7 @@
std::string DefaultGuestImagePath(const std::string& file_name) {
return (cvd::StringFromEnv("ANDROID_PRODUCT_OUT",
- cvd::StringFromEnv("HOME", ".")) +
- "/") +
+ cvd::StringFromEnv("HOME", "."))) +
file_name;
}
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 142c257..2ff6500 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -282,11 +282,36 @@
void set_extra_kernel_cmdline(std::string extra_cmdline);
std::vector<std::string> extra_kernel_cmdline() const;
+ // A directory containing the SSL certificates for the signaling server
void set_webrtc_certs_dir(const std::string& certs_dir);
std::string webrtc_certs_dir() const;
- void set_dialog_certs_dir(const std::string& certs_dir);
- std::string dialog_certs_dir() const;
+ // The path to the webrtc signaling server binary
+ void set_sig_server_binary(const std::string& sig_server_binary);
+ std::string sig_server_binary() const;
+
+ // The port for the webrtc signaling server. It's used by the signaling server
+ // to bind to it and by the webrtc process to connect to and register itself
+ void set_sig_server_port(int port);
+ int sig_server_port() const;
+
+ // The address of the signaling server
+ void set_sig_server_address(const std::string& addr);
+ std::string sig_server_address() const;
+
+ // The path section of the url where the webrtc process registers itself with
+ // the signaling server
+ void set_sig_server_path(const std::string& path);
+ std::string sig_server_path() 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);
+ bool sig_server_strict() const;
+
+ // The dns address of mobile network (RIL)
+ void set_ril_dns(const std::string& ril_dns);
+ std::string ril_dns() const;
class InstanceSpecific;
class MutableInstanceSpecific;
@@ -336,6 +361,8 @@
int host_port() const;
// Port number to connect to the tpm server on the host
int tpm_port() const;
+ // Port number to connect to the keymaster server on the host
+ int keymaster_vsock_port() const;
std::string adb_ip_and_port() const;
std::string adb_device_name() const;
std::string device_title() const;
@@ -372,6 +399,15 @@
std::string launcher_log_path() const;
std::string launcher_monitor_socket_path() const;
+
+ std::string sdcard_path() const;
+
+ // The device id the webrtc process should use to register with the
+ // signaling server
+ std::string webrtc_device_id() const;
+
+ // Whether this instance should start the webrtc signaling server
+ bool start_webrtc_sig_server() const;
};
// A view into an existing CuttlefishConfig object for a particular instance.
@@ -393,6 +429,7 @@
void set_frames_server_port(int config_server_port);
void set_touch_server_port(int config_server_port);
void set_keyboard_server_port(int config_server_port);
+ void set_keymaster_vsock_port(int keymaster_vsock_port);
void set_host_port(int host_port);
void set_tpm_port(int tpm_port);
void set_adb_ip_and_port(const std::string& ip_port);
@@ -404,6 +441,8 @@
void set_uuid(const std::string& uuid);
void set_instance_dir(const std::string& instance_dir);
void set_virtual_disk_paths(const std::vector<std::string>& disk_paths);
+ void set_webrtc_device_id(const std::string& id);
+ void set_start_webrtc_signaling_server(bool start);
};
private:
@@ -430,7 +469,6 @@
std::string ForCurrentInstance(const char* prefix);
int ForCurrentInstance(int base);
-std::string GetDefaultPerInstanceDir();
std::string GetDefaultMempath();
int GetDefaultPerInstanceVsockCid();
diff --git a/host/libs/config/kernel_args.cpp b/host/libs/config/kernel_args.cpp
index 531fc99..b6590ba 100644
--- a/host/libs/config/kernel_args.cpp
+++ b/host/libs/config/kernel_args.cpp
@@ -87,7 +87,7 @@
kernel_cmdline.push_back(concat("androidboot.cuttlefish_config_server_port=", instance.config_server_port()));
}
- if (instance.tpm_port()) {
+ if (config.tpm_binary() != "" && instance.tpm_port()) {
kernel_cmdline.push_back(concat("androidboot.tpm_vsock_port=", instance.tpm_port()));
}
@@ -103,6 +103,9 @@
kernel_cmdline.push_back(concat("androidboot.vsock_frames_port=", instance.frames_server_port()));
}
+ kernel_cmdline.push_back(concat("androidboot.vsock_keymaster_port=",
+ instance.keymaster_vsock_port()));
+
AppendVector(&kernel_cmdline, config.extra_kernel_cmdline());
return kernel_cmdline;
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index fe49981..206789f 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -169,7 +169,7 @@
if (!config_->final_ramdisk_path().empty()) {
crosvm_cmd.AddParameter("--initrd=", config_->final_ramdisk_path());
}
- crosvm_cmd.AddParameter("--null-audio");
+ // crosvm_cmd.AddParameter("--null-audio");
crosvm_cmd.AddParameter("--mem=", config_->memory_mb());
crosvm_cmd.AddParameter("--cpus=", config_->cpus());
crosvm_cmd.AddParameter("--params=", kernel_cmdline_);
diff --git a/host_package.mk b/host_package.mk
index 18176c5..5f266e8 100644
--- a/host_package.mk
+++ b/host_package.mk
@@ -45,9 +45,6 @@
x86_64-linux-gnu/libandroid-emu-shared.so \
x86_64-linux-gnu/libemugl_common.so \
x86_64-linux-gnu/libOpenglRender.so \
- x86_64-linux-gnu/libEGL_translator.so \
- x86_64-linux-gnu/libGLES_CM_translator.so \
- x86_64-linux-gnu/libGLES_V2_translator.so \
x86_64-linux-gnu/libgfxstream_backend.so \
logcat_receiver \
config_server \
@@ -57,6 +54,7 @@
run_cvd \
cvd_status \
webRTC \
+ webrtc_sig_server \
metrics \
fsck.f2fs \
resize.f2fs \
@@ -64,7 +62,11 @@
tpm_simulator_manager \
vtpm_passthrough \
ms-tpm-20-ref \
- lz4
+ lz4 \
+ mkenvimage \
+ tapsetiff \
+ newfs_msdos \
+ secure_env \
cvd_host_tests := \
monotonic_time_test \
@@ -97,6 +99,10 @@
libopus.so \
libyuv.so \
libjpeg.so \
+ libkeymaster_messages.so \
+ libkeymaster_portable.so \
+ libsoft_attestation_cert.so \
+ libcuttlefish_security.so \
webrtc_assets := \
index.html \
diff --git a/recovery/Android.bp b/recovery/Android.bp
new file mode 100644
index 0000000..5e495d7
--- /dev/null
+++ b/recovery/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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.
+//
+
+cc_library_static {
+ name: "librecovery_ui_cuttlefish",
+ owner: "google",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-pedantic",
+ ],
+ srcs: [
+ "recovery_ui.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "librecovery_ui",
+ ],
+}
diff --git a/recovery/recovery_ui.cpp b/recovery/recovery_ui.cpp
new file mode 100644
index 0000000..479a85f
--- /dev/null
+++ b/recovery/recovery_ui.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 <recovery_ui/device.h>
+#include <recovery_ui/screen_ui.h>
+
+class CuttlefishRecoveryUI : public ScreenRecoveryUI {
+ public:
+ CuttlefishRecoveryUI() : ScreenRecoveryUI() {}
+
+ bool IsUsbConnected() override {
+ return true;
+ }
+};
+
+Device* make_device() {
+ return new Device(new CuttlefishRecoveryUI);
+}
diff --git a/required_images b/required_images
new file mode 100644
index 0000000..d07652e
--- /dev/null
+++ b/required_images
@@ -0,0 +1,7 @@
+boot.img
+cache.img
+super.img
+userdata.img
+vbmeta.img
+vbmeta_system.img
+vendor_boot.img
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index 542ada7..1d79fd9 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -20,13 +20,6 @@
include build/make/target/board/BoardConfigMainlineCommon.mk
-# Reset CF unsupported settings
-TARGET_NO_RECOVERY := false
-BOARD_USES_SYSTEM_OTHER_ODEX :=
-WITH_DEXPREOPT := true
-BOARD_AVB_ENABLE := false
-
-
TARGET_BOOTLOADER_BOARD_NAME := cutf
# Boot partition size: 32M
@@ -34,6 +27,7 @@
# will not change (as is it not a filesystem.)
BOARD_BOOTIMAGE_PARTITION_SIZE := 67108864
BOARD_RECOVERYIMAGE_PARTITION_SIZE := 67108864
+BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE := 67108864
# Build a separate vendor.img partition
BOARD_USES_VENDORIMAGE := true
@@ -55,6 +49,16 @@
BOARD_ODMIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_COPY_OUT_ODM := odm
+# FIXME: Remove this once we generate the vbmeta digest correctly
+BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --flag 2
+
+# Enable chained vbmeta for system image mixing
+BOARD_AVB_VBMETA_SYSTEM := product system system_ext
+BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
+BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA2048
+BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
+BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION := 1
+
BOARD_USES_GENERIC_AUDIO := false
USE_CAMERA_STUB := true
TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true
@@ -75,9 +79,6 @@
BOARD_CACHEIMAGE_PARTITION_SIZE := 67108864
BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
-# Use ext4 block sharing on read-only partitions
-BOARD_EXT4_SHARE_DUP_BLOCKS := true
-
BOARD_GPU_DRIVERS := virgl
# Enable goldfish's encoder.
@@ -107,9 +108,15 @@
WIFI_DRIVER_FW_PATH_STA := "/dev/null"
WIFI_DRIVER_FW_PATH_AP := "/dev/null"
-BOARD_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/vendor
-BOARD_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/vendor/google
-PRODUCT_PRIVATE_SEPOLICY_DIRS := device/google/cuttlefish/shared/sepolicy/private
+# vendor sepolicy
+BOARD_VENDOR_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/vendor
+BOARD_VENDOR_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/vendor/google
+# product sepolicy, allow other layers to append
+PRODUCT_PRIVATE_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/product/private
+# PRODUCT_PUBLIC_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/product/public
+# system_ext sepolicy
+# BOARD_PLAT_PRIVATE_SEPOLICY_DIR += device/google/cuttlefish/shared/sepolicy/system_ext/private
+# BOARD_PLAT_PUBLIC_SEPOLICY_DIR := device/google/cuttlefish/shared/sepolicy/system_ext/public
VSOC_STLPORT_INCLUDES :=
VSOC_STLPORT_LIBS :=
@@ -144,7 +151,7 @@
TARGET_RECOVERY_PIXEL_FORMAT := ABGR_8888
-
+TARGET_RECOVERY_UI_LIB := librecovery_ui_cuttlefish
TARGET_RECOVERY_FSTAB ?= device/google/cuttlefish/shared/config/fstab
BOARD_SUPER_PARTITION_SIZE := 6442450944
@@ -169,5 +176,8 @@
BOARD_BOOT_HEADER_VERSION := 3
BOARD_USES_RECOVERY_AS_BOOT := true
BOARD_MKBOOTIMG_ARGS += --header_version $(BOARD_BOOT_HEADER_VERSION)
-PRODUCT_COPY_FILES += device/google/cuttlefish/dtb.img:dtb.img
+PRODUCT_COPY_FILES += \
+ device/google/cuttlefish/dtb.img:dtb.img \
+ device/google/cuttlefish/required_images:required_images \
+
BOARD_BUILD_SYSTEM_ROOT_IMAGE := false
diff --git a/shared/auto/device.mk b/shared/auto/device.mk
index e0693e5..17991aa 100644
--- a/shared/auto/device.mk
+++ b/shared/auto/device.mk
@@ -22,6 +22,9 @@
$(call inherit-product, device/google/cuttlefish/shared/device.mk)
+# Extend cuttlefish common sepolicy with auto-specific functionality
+BOARD_SEPOLICY_DIRS += device/google/cuttlefish/shared/auto/sepolicy/vendor
+
################################################
# Begin general Android Auto Embedded configurations
@@ -29,31 +32,19 @@
packages/services/Car/car_product/init/init.bootstat.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw//init.bootstat.rc \
packages/services/Car/car_product/init/init.car.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw//init.car.rc
-# Auto core hardware permissions
PRODUCT_COPY_FILES += \
+ frameworks/native/data/etc/android.hardware.broadcastradio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.broadcastradio.xml \
+ frameworks/native/data/etc/android.hardware.screen.landscape.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.screen.landscape.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 \
+ frameworks/native/data/etc/android.software.activities_on_secondary_displays.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.activities_on_secondary_displays.xml \
frameworks/native/data/etc/car_core_hardware.xml:system/etc/permissions/car_core_hardware.xml \
- frameworks/native/data/etc/android.hardware.type.automotive.xml:system/etc/permissions/android.hardware.type.automotive.xml \
-
-# Enable landscape
-PRODUCT_COPY_FILES += \
- frameworks/native/data/etc/android.hardware.screen.landscape.xml:system/etc/permissions/android.hardware.screen.landscape.xml
-
-# Used to embed a map in an activity view
-PRODUCT_COPY_FILES += \
- frameworks/native/data/etc/android.software.activities_on_secondary_displays.xml:system/etc/permissions/android.software.activities_on_secondary_displays.xml
-
-# Location permissions
-PRODUCT_COPY_FILES += \
- frameworks/native/data/etc/android.hardware.location.gps.xml:system/etc/permissions/android.hardware.location.gps.xml
-
-# Broadcast Radio permissions
-PRODUCT_COPY_FILES += \
- frameworks/native/data/etc/android.hardware.broadcastradio.xml:system/etc/permissions/android.hardware.broadcastradio.xml
PRODUCT_PROPERTY_OVERRIDES += \
keyguard.no_require_sim=true \
ro.cdma.home.operator.alpha=Android \
ro.cdma.home.operator.numeric=302780 \
+ ro.com.android.dataroaming=true \
vendor.rild.libpath=libcuttlefish-ril.so \
# vehicle HAL
@@ -65,17 +56,6 @@
# Broadcast Radio
PRODUCT_PACKAGES += android.hardware.broadcastradio@2.0-service
-# DRM HAL
-PRODUCT_PACKAGES += android.hardware.drm@1.2-service.clearkey
-
-# GPS HAL
-PRODUCT_PACKAGES += \
- android.hardware.gnss@2.0-service
-
-# DRM Properities
-PRODUCT_PROPERTY_OVERRIDES += \
- drm.service.enabled=true
-
BOARD_IS_AUTOMOTIVE := true
$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base.mk)
diff --git a/shared/auto/sepolicy/vendor/file_contexts b/shared/auto/sepolicy/vendor/file_contexts
new file mode 100644
index 0000000..e5fcacd
--- /dev/null
+++ b/shared/auto/sepolicy/vendor/file_contexts
@@ -0,0 +1 @@
+/vendor/lib(64)?/cuttlefish_auto_resources.so u:object_r:same_process_hal_file:s0
diff --git a/shared/sepolicy/vendor/hal_vehicle_default.te b/shared/auto/sepolicy/vendor/hal_vehicle_default.te
similarity index 100%
rename from shared/sepolicy/vendor/hal_vehicle_default.te
rename to shared/auto/sepolicy/vendor/hal_vehicle_default.te
diff --git a/shared/config/fstab b/shared/config/fstab
index 715c68b..a9a6718 100644
--- a/shared/config/fstab
+++ b/shared/config/fstab
@@ -1,14 +1,16 @@
-boot /boot emmc defaults recoveryonly
-system /system ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
+/dev/block/by-name/boot /boot emmc defaults recoveryonly,slotselect
+/dev/block/by-name/vendor_boot /vendor_boot emmc defaults recoveryonly,slotselect
+system /system ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta_system
# Add all non-dynamic partitions except system, after this comment
/dev/block/by-name/userdata /data f2fs nodev,noatime,nosuid,inlinecrypt,reserve_root=32768 latemount,wait,fileencryption=aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized,fsverity,keydirectory=/metadata/vold/metadata_encryption
/dev/block/by-name/cache /cache ext4 nodev,noatime,nosuid,errors=panic wait
/dev/block/by-name/metadata /metadata ext4 nodev,noatime,nosuid,errors=panic wait,formattable,first_stage_mount
/dev/block/by-name/misc /misc emmc defaults defaults
# Add all dynamic partitions except system, after this comment
-odm /odm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
-product /product ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
-system_ext /system_ext ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
-vendor /vendor ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
+odm /odm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
+product /product ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
+system_ext /system_ext ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta_system
+vendor /vendor ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta
/dev/block/zram0 none swap defaults zramsize=75%
/tmp /sdcard none defaults,bind recoveryonly
+/devices/*/block/vdb auto auto defaults voldmanaged=sdcard1:auto,encryptable=userdata
diff --git a/shared/config/fstab.ext4 b/shared/config/fstab.ext4
index 3e3672f..f2ffbfa 100644
--- a/shared/config/fstab.ext4
+++ b/shared/config/fstab.ext4
@@ -1,14 +1,16 @@
-boot /boot emmc defaults recoveryonly
-system /system ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
+boot /boot emmc defaults recoveryonly,slotselect
+vendor_boot /vendor_boot emmc defaults recoveryonly,slotselect
+system /system ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta_system
# Add all non-dynamic partitions except system, after this comment
/dev/block/by-name/userdata /data ext4 nodev,noatime,nosuid,errors=panic wait,fileencryption=aes-256-xts:aes-256-cts,fsverity
/dev/block/by-name/cache /cache ext4 nodev,noatime,nosuid,errors=panic wait
/dev/block/by-name/metadata /metadata ext4 nodev,noatime,nosuid,errors=panic wait,formattable,first_stage_mount
/dev/block/by-name/misc /misc emmc defaults defaults
# Add all dynamic partitions except system, after this comment
-odm /odm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
-product /product ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
-system_ext /system_ext ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
-vendor /vendor ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
+odm /odm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
+product /product ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
+system_ext /system_ext ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta_system
+vendor /vendor ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta
/dev/block/zram0 none swap defaults zramsize=75%
/tmp /sdcard none defaults,bind recoveryonly
+/devices/*/block/vdb auto auto defaults voldmanaged=sdcard1:auto,encryptable=userdata
diff --git a/shared/config/init.vendor.rc b/shared/config/init.vendor.rc
index 885d76b..bd555c8 100644
--- a/shared/config/init.vendor.rc
+++ b/shared/config/init.vendor.rc
@@ -1,15 +1,17 @@
on early-init
# loglevel 8
- symlink /sdcard /storage/sdcard0
mkdir /var/run 0755 root root
mkdir /var/run/media 0755 media root
mkdir /var/run/system 0755 system root
mkdir /dev/gce 0750
chown system system /dev/gce
- mount tracefs tracefs /sys/kernel/tracing
mount securityfs securityfs /sys/kernel/security
+ # For KCOV
+ mount debugfs debugfs /sys/kernel/debug
+ chmod 0755 /sys/kernel/debug
+
setprop ro.sf.lcd_density ${ro.boot.lcd_density}
setprop ro.hardware.egl ${ro.boot.hardware.egl}
setprop ro.hardware.gralloc ${ro.boot.hardware.gralloc}
diff --git a/shared/config/manifest.xml b/shared/config/manifest.xml
index 2216680..dff0d66 100644
--- a/shared/config/manifest.xml
+++ b/shared/config/manifest.xml
@@ -17,7 +17,6 @@
*/
-->
<manifest version="1.0" type="device" target-level="6">
- <kernel target-level="6" />
<hal format="hidl">
<name>android.hardware.audio</name>
<transport>hwbinder</transport>
diff --git a/shared/device.mk b/shared/device.mk
index c0023c6..236482a 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -20,8 +20,7 @@
# Enable updating of APEXes
$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
-# Enable userspace reboot
-$(call inherit-product, $(SRC_TARGET_DIR)/product/userspace_reboot.mk)
+PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for vulkan
PRODUCT_SHIPPING_API_LEVEL := 31
PRODUCT_BUILD_BOOT_IMAGE := true
@@ -36,6 +35,8 @@
product \
system \
system_ext \
+ vbmeta \
+ vbmeta_system \
vendor
# Enable Virtual A/B
@@ -68,19 +69,28 @@
ro.opengles.version=196608 \
wifi.interface=wlan0 \
persist.sys.zram_enabled=1 \
- ro.apk_verity.mode=2 \
ro.rebootescrow.device=/dev/block/pmem0 \
# Below is a list of properties we probably should get rid of.
PRODUCT_PROPERTY_OVERRIDES += \
wlan.driver.status=ok
+# Codec 2.0 is unstable on x86
+PRODUCT_PROPERTY_OVERRIDES += \
+ debug.stagefright.ccodec=0
+
# Enforce privapp-permissions whitelist.
PRODUCT_PROPERTY_OVERRIDES += ro.control_privapp_permissions=enforce
# aes-256-heh default is not supported in standard kernels.
PRODUCT_PROPERTY_OVERRIDES += ro.crypto.volume.filenames_mode=aes-256-cts
+# Copy preopted files from system_b on first boot
+PRODUCT_PROPERTY_OVERRIDES += ro.cp_system_other_odex=1
+
+# DRM service opt-in
+PRODUCT_PROPERTY_OVERRIDES += drm.service.enabled=true
+
# Packages for various GCE-specific utilities
#
PRODUCT_PACKAGES += \
@@ -167,6 +177,7 @@
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 \
device/google/cuttlefish/shared/config/media_profiles.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_profiles_V1_0.xml \
+ device/google/cuttlefish/shared/permissions/cuttlefish_excluded_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/cuttlefish_excluded_hardware.xml \
device/google/cuttlefish/shared/permissions/privapp-permissions-cuttlefish.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/privapp-permissions-cuttlefish.xml \
frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \
frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_audio.xml \
@@ -179,25 +190,25 @@
frameworks/av/services/audiopolicy/config/surround_sound_configuration_5_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/surround_sound_configuration_5_0.xml \
frameworks/native/data/etc/android.hardware.audio.low_latency.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.audio.low_latency.xml \
frameworks/native/data/etc/android.hardware.bluetooth_le.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.bluetooth_le.xml \
- frameworks/native/data/etc/android.hardware.bluetooth.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.bluetooth.xml \
- frameworks/native/data/etc/android.hardware.camera.flash-autofocus.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.xml \
- frameworks/native/data/etc/android.hardware.camera.full.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.full.xml \
+ frameworks/native/data/etc/android.hardware.camera.flash-autofocus.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.flash-autofocus.xml \
frameworks/native/data/etc/android.hardware.camera.front.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.front.xml \
+ frameworks/native/data/etc/android.hardware.camera.full.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.full.xml \
frameworks/native/data/etc/android.hardware.camera.raw.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.raw.xml \
- frameworks/native/data/etc/android.hardware.ethernet.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.ethernet.xml \
+ frameworks/native/data/etc/android.hardware.faketouch.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.faketouch.xml \
frameworks/native/data/etc/android.hardware.location.gps.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.location.gps.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.ambient_temperature.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.ambient_temperature.xml \
frameworks/native/data/etc/android.hardware.sensor.barometer.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.barometer.xml \
- frameworks/native/data/etc/android.hardware.sensor.compass.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.compass.xml \
frameworks/native/data/etc/android.hardware.sensor.gyroscope.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.gyroscope.xml \
frameworks/native/data/etc/android.hardware.sensor.light.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.light.xml \
frameworks/native/data/etc/android.hardware.sensor.proximity.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.proximity.xml \
- frameworks/native/data/etc/android.hardware.touchscreen.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.touchscreen.xml \
+ frameworks/native/data/etc/android.hardware.sensor.relative_humidity.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.relative_humidity.xml \
frameworks/native/data/etc/android.hardware.usb.accessory.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.usb.accessory.xml \
frameworks/native/data/etc/android.hardware.vulkan.level-0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.vulkan.level.xml \
frameworks/native/data/etc/android.hardware.vulkan.version-1_0_3.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.vulkan.version.xml \
frameworks/native/data/etc/android.hardware.wifi.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.wifi.xml \
- frameworks/native/data/etc/android.software.app_widgets.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.app_widgets.xml \
+ frameworks/native/data/etc/android.software.ipsec_tunnels.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.ipsec_tunnels.xml \
+ 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 \
frameworks/native/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.vulkan.deqp.level.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 \
diff --git a/shared/permissions/cuttlefish_excluded_hardware.xml b/shared/permissions/cuttlefish_excluded_hardware.xml
new file mode 100644
index 0000000..3660289
--- /dev/null
+++ b/shared/permissions/cuttlefish_excluded_hardware.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<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/device.mk b/shared/phone/device.mk
index 4e183f7..6f377eb 100644
--- a/shared/phone/device.mk
+++ b/shared/phone/device.mk
@@ -22,8 +22,6 @@
$(call inherit-product, frameworks/native/build/phone-xhdpi-2048-dalvik-heap.mk)
$(call inherit-product, device/google/cuttlefish/shared/device.mk)
-PRODUCT_CHARACTERISTICS := nosdcard
-
PRODUCT_PROPERTY_OVERRIDES += \
keyguard.no_require_sim=true \
ro.cdma.home.operator.alpha=Android \
diff --git a/shared/phone/device_vendor.mk b/shared/phone/device_vendor.mk
index 7785e7d..1eb9ee8 100644
--- a/shared/phone/device_vendor.mk
+++ b/shared/phone/device_vendor.mk
@@ -27,8 +27,6 @@
$(call inherit-product, frameworks/native/build/phone-xhdpi-2048-dalvik-heap.mk)
$(call inherit-product, device/google/cuttlefish/shared/device.mk)
-PRODUCT_CHARACTERISTICS := nosdcard
-
PRODUCT_PROPERTY_OVERRIDES += \
keyguard.no_require_sim=true \
ro.cdma.home.operator.alpha=Android \
diff --git a/shared/sepolicy/private/file_contexts b/shared/sepolicy/product/private/file_contexts
similarity index 100%
rename from shared/sepolicy/private/file_contexts
rename to shared/sepolicy/product/private/file_contexts
diff --git a/shared/sepolicy/private/property_contexts b/shared/sepolicy/product/private/property_contexts
similarity index 100%
rename from shared/sepolicy/private/property_contexts
rename to shared/sepolicy/product/private/property_contexts
diff --git a/shared/sepolicy/private/suspend_blocker.te b/shared/sepolicy/product/private/suspend_blocker.te
similarity index 62%
rename from shared/sepolicy/private/suspend_blocker.te
rename to shared/sepolicy/product/private/suspend_blocker.te
index 41c72b9..fd2fcdc 100644
--- a/shared/sepolicy/private/suspend_blocker.te
+++ b/shared/sepolicy/product/private/suspend_blocker.te
@@ -1,5 +1,5 @@
type suspend_blocker, domain, coredomain;
-type suspend_blocker_exec, exec_type, file_type;
+type suspend_blocker_exec, exec_type, system_file_type, file_type;
init_daemon_domain(suspend_blocker);
diff --git a/shared/sepolicy/private/tombstone_transmit.te b/shared/sepolicy/product/private/tombstone_transmit.te
similarity index 84%
rename from shared/sepolicy/private/tombstone_transmit.te
rename to shared/sepolicy/product/private/tombstone_transmit.te
index a17ed10..c182270 100644
--- a/shared/sepolicy/private/tombstone_transmit.te
+++ b/shared/sepolicy/product/private/tombstone_transmit.te
@@ -1,5 +1,5 @@
type tombstone_transmit, domain, coredomain;
-type tombstone_transmit_exec, exec_type, file_type;
+type tombstone_transmit_exec, exec_type, system_file_type, file_type;
init_daemon_domain(tombstone_transmit);
diff --git a/shared/sepolicy/vendor/bug_map b/shared/sepolicy/vendor/bug_map
index 81351af..e8f546e 100644
--- a/shared/sepolicy/vendor/bug_map
+++ b/shared/sepolicy/vendor/bug_map
@@ -1,11 +1,7 @@
-gsid gsid capability b/146356992
init system_lib_file dir b/133444385
init system_lib_file file b/133444385
logpersist logpersist capability b/132911257
logpersist device file b/143108875
migrate_legacy_obb_data dalvikcache_data_file file b/152338071
-platform_app radio_prop property_service b/140284352
-priv_app proc_net file b/124422390
shell adbd vsock_socket b/131904985
system_server system_server process b/65201432
-zygote ramdump_app process b/139558100
diff --git a/shared/sepolicy/vendor/device.te b/shared/sepolicy/vendor/device.te
index 38f0a2f..e63eddf 100644
--- a/shared/sepolicy/vendor/device.te
+++ b/shared/sepolicy/vendor/device.te
@@ -1,7 +1,3 @@
# Device types
-type input_events_device, dev_type;
-type libcuttlefish_rild_device, dev_type;
-type region_e2e_test_device, dev_type;
-type region_screen_device, dev_type;
-type socket_forward_device, dev_type;
+type ab_block_device, dev_type;
type virtual_serial_device, dev_type;
diff --git a/shared/sepolicy/vendor/dumpstate.te b/shared/sepolicy/vendor/dumpstate.te
new file mode 100644
index 0000000..34caf72
--- /dev/null
+++ b/shared/sepolicy/vendor/dumpstate.te
@@ -0,0 +1 @@
+allow dumpstate hal_neuralnetworks_sample:process signal;
diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts
index 7041961..2f6afe9 100644
--- a/shared/sepolicy/vendor/file_contexts
+++ b/shared/sepolicy/vendor/file_contexts
@@ -3,40 +3,41 @@
#
# crosvm (x86) block devices
-/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/boot u:object_r:boot_block_device:s0
-/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/metadata u:object_r:metadata_block_device:s0
/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/misc u:object_r:misc_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/boot_[ab] u:object_r:boot_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/vendor_boot_[ab] u:object_r:boot_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/verity_[ab] u:object_r:ab_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/verity_system_[ab] u:object_r:ab_block_device:s0
/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/super u:object_r:super_block_device:s0
/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/userdata u:object_r:userdata_block_device:s0
/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/cache u:object_r:cache_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:01\.0/by-name/metadata u:object_r:metadata_block_device:s0
# crosvm (arm64) block devices
-/dev/block/platform/10000.pci/by-name/boot u:object_r:boot_block_device:s0
-/dev/block/platform/10000.pci/by-name/metadata u:object_r:metadata_block_device:s0
/dev/block/platform/10000.pci/by-name/misc u:object_r:misc_block_device:s0
+/dev/block/platform/10000.pci/by-name/boot_[ab] u:object_r:boot_block_device:s0
+/dev/block/platform/10000.pci/by-name/vendor_boot_[ab] u:object_r:boot_block_device:s0
+/dev/block/platform/10000.pci/by-name/verity_[ab] u:object_r:ab_block_device:s0
+/dev/block/platform/10000.pci/by-name/verity_system_[ab] u:object_r:ab_block_device:s0
/dev/block/platform/10000.pci/by-name/super u:object_r:super_block_device:s0
/dev/block/platform/10000.pci/by-name/userdata u:object_r:userdata_block_device:s0
/dev/block/platform/10000.pci/by-name/cache u:object_r:cache_block_device:s0
+/dev/block/platform/10000.pci/by-name/metadata u:object_r:metadata_block_device:s0
# qemu block devices
-/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/boot u:object_r:boot_block_device:s0
-/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/metadata u:object_r:metadata_block_device:s0
/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/misc u:object_r:misc_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/boot_[ab] u:object_r:boot_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/vendor_boot_[ab] u:object_r:boot_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/verity_[ab] u:object_r:ab_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/verity_system_[ab] u:object_r:ab_block_device:s0
/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/super u:object_r:super_block_device:s0
/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/userdata u:object_r:userdata_block_device:s0
/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/cache u:object_r:cache_block_device:s0
+/dev/block/pci/pci0000:00/0000:00:03\.0/by-name/metadata u:object_r:metadata_block_device:s0
/dev/block/pmem0 u:object_r:rebootescrow_device:s0
/dev/block/zram0 u:object_r:swap_block_device:s0
/dev/dri u:object_r:gpu_device:s0
/dev/dri/card0 u:object_r:graphics_device:s0
/dev/dri/renderD128 u:object_r:gpu_device:s0
-/dev/e2e_managed u:object_r:region_e2e_test_device:s0
-/dev/e2e_manager u:object_r:region_e2e_test_device:s0
-/dev/e2e_primary u:object_r:region_e2e_test_device:s0
-/dev/e2e_secondary u:object_r:region_e2e_test_device:s0
-/dev/input_events u:object_r:input_events_device:s0
-/dev/ril u:object_r:libcuttlefish_rild_device:s0
-/dev/screen u:object_r:region_screen_device:s0
-/dev/socket_forward u:object_r:socket_forward_device:s0
/dev/vport[0-9]p[0-9]* u:object_r:virtual_serial_device:s0
/dev/vtpmx u:object_r:vtpm_creation_device:s0
/dev/tpmrm0 u:object_r:tpm_resource_manager:s0
@@ -75,7 +76,6 @@
/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\.vibrator@1\.x-service\.example u:object_r:hal_vibrator_default_exec:s0
-/vendor/bin/hw/android\.hardware\.tv\.cec@1\.0-service\.mock u:object_r:hal_tv_cec_mock_exec:s0
/vendor/bin/ip_link_add u:object_r:ip_link_add_exec:s0
/vendor/bin/setup_wifi u:object_r:setup_wifi_exec:s0
/vendor/bin/hw/android\.hardware\.sensors@2\.0-service\.mock u:object_r:hal_sensors_default_exec:s0
@@ -84,7 +84,6 @@
/vendor/bin/hw/android\.hardware\.authsecret@1\.0-service u:object_r:hal_authsecret_default_exec:s0
/vendor/bin/init\.insmod\.sh u:object_r:init_insmod_sh_exec:s0
-/vendor/lib(64)?/cuttlefish_auto_resources.so u:object_r:same_process_hal_file: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
diff --git a/shared/sepolicy/vendor/genfs_contexts b/shared/sepolicy/vendor/genfs_contexts
index 5ac5257..ba60da8 100644
--- a/shared/sepolicy/vendor/genfs_contexts
+++ b/shared/sepolicy/vendor/genfs_contexts
@@ -1,35 +1,52 @@
# crosvm (x86)
-genfscon sysfs /devices/pci0000:00/0000:00:07.0/virtio6/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
-genfscon sysfs /devices/pci0000:00/0000:00:08.0/virtio7/net u:object_r:sysfs_net:s0 # rmnet0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/device u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/subsystem_device u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/subsystem_vendor u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/uevent u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/vendor u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/pnp0/00:00/rtc u:object_r:sysfs_rtc:s0 # also used by qemu
+genfscon sysfs /devices/pci0000:00/0000:00:08.0/virtio7/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
+genfscon sysfs /devices/pci0000:00/0000:00:09.0/virtio8/net u:object_r:sysfs_net:s0 # rmnet0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/device u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/subsystem_device u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/subsystem_vendor u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/uevent u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/vendor u:object_r:sysfs_gpu:s0
+## find /sys/devices/platform/* -type d -name 'rtc[0-9]' | sed 's,/rtc[0-9],,'
+## x86 rtc_cmos on crosvm does not currently expose rtcN/hctosys
+## find /sys/devices/platform/* -type d -name 'wakeup[0-9]'
+genfscon sysfs /devices/platform/rtc_cmos/wakeup/wakeup0 u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup1 u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc1/wakeup2 u:object_r:sysfs_wakeup:s0 # <= 5.5
+genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc1/alarmtimer.0.auto/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0 # >5.5
+genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup3 u:object_r:sysfs_wakeup:s0
+
# crosvm (arm64)
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:07.0/virtio6/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:08.0/virtio7/net u:object_r:sysfs_net:s0 # rmnet0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/device u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/subsystem_device u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/subsystem_vendor u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/uevent u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/vendor u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/platform/2000.rtc/rtc u:object_r:sysfs_rtc:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:08.0/virtio7/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:09.0/virtio8/net u:object_r:sysfs_net:s0 # rmnet0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/device u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/subsystem_device u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/subsystem_vendor u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/uevent u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/vendor u:object_r:sysfs_gpu:s0
+## 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]'
+## arm64 2000.rtc on crosvm does not currently expose a wakeup node
+genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup0 u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc2/wakeup1 u:object_r:sysfs_wakeup:s0 # <= 5.5
+genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc2/alarmtimer.0.auto/wakeup/wakeup1 u:object_r:sysfs_wakeup:s0 # >5.5
+genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0
+
# qemu (x86)
-genfscon sysfs /devices/pci0000:00/0000:00:04.0/virtio2/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
-genfscon sysfs /devices/pci0000:00/0000:00:05.0/virtio3/net u:object_r:sysfs_net:s0 # rmnet0
+genfscon sysfs /devices/pci0000:00/0000:00:05.0/virtio3/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
+genfscon sysfs /devices/pci0000:00/0000:00:06.0/virtio4/net u:object_r:sysfs_net:s0 # rmnet0
+# FIXME: Add sysfs_gpu labels for qemu
+## find /sys/devices/platform/* -type d -name 'rtc[0-9]' | sed 's,/rtc[0-9],,'
+genfscon sysfs /devices/pnp0/00:00/rtc u:object_r:sysfs_rtc:s0
+## find /sys/devices/platform/* -type d -name 'wakeup[0-9][0-9]'
+genfscon sysfs /devices/pnp0/00:00/wakeup/wakeup13 u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/pnp0/00:00/rtc/rtc0/wakeup14 u:object_r:sysfs_wakeup:s0 # <= 5.5
+genfscon sysfs /devices/pnp0/00:00/rtc/rtc0/alarmtimer.0.auto/wakeup/wakeup14 u:object_r:sysfs_wakeup:s0 # >5.5
+genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup15 u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup16 u:object_r:sysfs_wakeup:s0
# common on all platforms / vm managers
+genfscon sysfs /devices/platform/rtc-test.0/rtc u:object_r:sysfs_rtc:s0
+genfscon sysfs /devices/platform/rtc-test.1/rtc u:object_r:sysfs_rtc:s0
+genfscon sysfs /devices/platform/rtc-test.2/rtc u:object_r:sysfs_rtc:s0
genfscon sysfs /bus/iio/devices u:object_r:sysfs_iio_devices:s0
-genfscon sysfs /devices/platform/rtc-test.0/rtc/rtc0/hctosys u:object_r:sysfs_rtc:s0
-genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc1/hctosys u:object_r:sysfs_rtc:s0
-genfscon sysfs /devices/platform/rtc-test.2/rtc/rtc2/hctosys u:object_r:sysfs_rtc:s0
-# TODO(b/148802006): Work around core policy sysfs_wakeup label not working
-# All kernels
-genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup0 u:object_r:sysfs_wakeup:s0
-genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0
-# Kernels <=5.5
-genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc1/wakeup1 u:object_r:sysfs_wakeup:s0
-# Kernels >5.5
-genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc1/alarmtimer.0.auto/wakeup/wakeup1 u:object_r:sysfs_wakeup:s0
diff --git a/shared/sepolicy/vendor/google/bug_map b/shared/sepolicy/vendor/google/bug_map
new file mode 100644
index 0000000..26c5581
--- /dev/null
+++ b/shared/sepolicy/vendor/google/bug_map
@@ -0,0 +1 @@
+zygote ramdump_app process b/139558100
diff --git a/shared/sepolicy/vendor/hal_bootctl_default.te b/shared/sepolicy/vendor/hal_bootctl_default.te
new file mode 100644
index 0000000..e727add
--- /dev/null
+++ b/shared/sepolicy/vendor/hal_bootctl_default.te
@@ -0,0 +1 @@
+allow hal_bootctl_default boot_block_device:blk_file getattr;
diff --git a/shared/sepolicy/vendor/hal_graphics_allocator.te b/shared/sepolicy/vendor/hal_graphics_allocator.te
index 3136386..5975599 100644
--- a/shared/sepolicy/vendor/hal_graphics_allocator.te
+++ b/shared/sepolicy/vendor/hal_graphics_allocator.te
@@ -3,4 +3,3 @@
# Read GCE initial metadata file
allow hal_graphics_allocator_server initial_metadata_file:file r_file_perms;
-allow hal_graphics_allocator_server region_screen_device:chr_file rw_file_perms;
diff --git a/shared/sepolicy/vendor/hal_graphics_composer.te b/shared/sepolicy/vendor/hal_graphics_composer.te
index 5b4f974..6bba59d 100644
--- a/shared/sepolicy/vendor/hal_graphics_composer.te
+++ b/shared/sepolicy/vendor/hal_graphics_composer.te
@@ -1,7 +1,6 @@
type vsock_frames_port_prop, property_type;
allow hal_graphics_composer_server hal_graphics_allocator_default_tmpfs:file read;
-allow hal_graphics_composer_server region_screen_device:chr_file rw_file_perms;
allow hal_graphics_composer_server self:{ socket vsock_socket } create_socket_perms_no_ioctl;
gpu_access(hal_graphics_composer_server)
diff --git a/shared/sepolicy/vendor/libcuttlefish_rild.te b/shared/sepolicy/vendor/libcuttlefish_rild.te
index 8894919..49b7d50 100644
--- a/shared/sepolicy/vendor/libcuttlefish_rild.te
+++ b/shared/sepolicy/vendor/libcuttlefish_rild.te
@@ -5,8 +5,6 @@
hal_server_domain(libcuttlefish_rild, hal_telephony)
-allow libcuttlefish_rild libcuttlefish_rild_device:chr_file rw_file_perms;
-
# Failing to create these sockets appears to be non-fatal
net_domain(libcuttlefish_rild)
diff --git a/shared/sepolicy/vendor/vendor_init.te b/shared/sepolicy/vendor/vendor_init.te
index e57bec0..f4fffe6 100644
--- a/shared/sepolicy/vendor/vendor_init.te
+++ b/shared/sepolicy/vendor/vendor_init.te
@@ -4,9 +4,6 @@
allow vendor_init {
audio_device
- input_events_device
- libcuttlefish_rild_device
- region_screen_device
}:chr_file { getattr };
set_prop(vendor_init, hal_bluetooth_sim_prop)
diff --git a/shared/sepolicy/vendor/vsoc_input_service.te b/shared/sepolicy/vendor/vsoc_input_service.te
index 341e503..ec20985 100644
--- a/shared/sepolicy/vendor/vsoc_input_service.te
+++ b/shared/sepolicy/vendor/vsoc_input_service.te
@@ -8,11 +8,6 @@
# I/O with /dev/uinput
allow vsoc_input_service uhid_device:chr_file rw_file_perms;
-# Framebuffer I/O (needed to obtain the screen size)
-allow vsoc_input_service region_screen_device:chr_file rw_file_perms;
-
-allow vsoc_input_service input_events_device:chr_file rw_file_perms;
-
net_domain(vsoc_input_service)
get_prop(vsoc_input_service, cuttlefish_config_server_port_prop)
diff --git a/shared/tv/device.mk b/shared/tv/device.mk
index 25eb85e..71b5d70 100644
--- a/shared/tv/device.mk
+++ b/shared/tv/device.mk
@@ -20,9 +20,19 @@
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_minimal.mk)
$(call inherit-product, device/google/cuttlefish/shared/device.mk)
+# Extend cuttlefish common sepolicy with tv-specific functionality
+BOARD_SEPOLICY_DIRS += device/google/cuttlefish/shared/tv/sepolicy/vendor
+
+PRODUCT_COPY_FILES += \
+ device/google/atv/permissions/tv_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/tv_core_hardware.xml \
+ frameworks/native/data/etc/android.hardware.bluetooth.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.bluetooth.xml \
+ 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 \
+ frameworks/native/data/etc/android.hardware.touchscreen.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.touchscreen.xml \
+
# HDMI CEC HAL
PRODUCT_PACKAGES += android.hardware.tv.cec@1.0-service.mock
# Enabling managed profiles
-PRODUCT_COPY_FILES += frameworks/native/data/etc/android.software.managed_users.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.managed_users.xml
DEVICE_PACKAGE_OVERLAYS += device/google/cuttlefish/shared/tv/overlay
diff --git a/shared/tv/sepolicy/vendor/file_contexts b/shared/tv/sepolicy/vendor/file_contexts
new file mode 100644
index 0000000..88b1cf5
--- /dev/null
+++ b/shared/tv/sepolicy/vendor/file_contexts
@@ -0,0 +1 @@
+/vendor/bin/hw/android\.hardware\.tv\.cec@1\.0-service\.mock u:object_r:hal_tv_cec_mock_exec:s0
diff --git a/shared/sepolicy/vendor/hal_tv_cec_mock.te b/shared/tv/sepolicy/vendor/hal_tv_cec_mock.te
similarity index 100%
rename from shared/sepolicy/vendor/hal_tv_cec_mock.te
rename to shared/tv/sepolicy/vendor/hal_tv_cec_mock.te
diff --git a/tools/create_base_image_gce.sh b/tools/create_base_image_gce.sh
index 7c693c5..6db2463 100755
--- a/tools/create_base_image_gce.sh
+++ b/tools/create_base_image_gce.sh
@@ -99,6 +99,8 @@
exit 1
fi
+# Vulkan loader
+sudo chroot /mnt/image /usr/bin/apt install -y libvulkan1
# Clean up the builder's version of resolv.conf
sudo rm /mnt/image/etc/resolv.conf