[automerge] radio: provide hardcoded IMEI and null IMEI-SV 2p: 5154019c1f am: fa19a971a2
Original change: https://googleplex-android-review.googlesource.com/c/device/google/cuttlefish/+/18620247
Change-Id: Ieef130f552d315d3bce71115940dcfcc44968b01
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/common/libs/device_config/host_device_config.cpp b/common/libs/device_config/host_device_config.cpp
index 35ec27f..eb28c63 100644
--- a/common/libs/device_config/host_device_config.cpp
+++ b/common/libs/device_config/host_device_config.cpp
@@ -163,8 +163,9 @@
device_display_config->set_width(cuttlefish_display_config.width);
device_display_config->set_height(cuttlefish_display_config.height);
- device_display_config->set_dpi(cuttlefish_config.dpi());
- device_display_config->set_refresh_rate_hz(cuttlefish_config.refresh_rate_hz());
+ device_display_config->set_dpi(cuttlefish_display_config.dpi);
+ device_display_config->set_refresh_rate_hz(
+ cuttlefish_display_config.refresh_rate_hz);
}
}
diff --git a/common/libs/fs/shared_fd.cpp b/common/libs/fs/shared_fd.cpp
index 8b21f0c..b770a38 100644
--- a/common/libs/fs/shared_fd.cpp
+++ b/common/libs/fs/shared_fd.cpp
@@ -444,7 +444,7 @@
return rval;
}
-SharedFD SharedFD::VsockServer(unsigned int port, int type) {
+SharedFD SharedFD::VsockServer(unsigned int port, int type, unsigned int cid) {
auto vsock = SharedFD::Socket(AF_VSOCK, type, 0);
if (!vsock->IsOpen()) {
return vsock;
@@ -452,7 +452,7 @@
sockaddr_vm addr{};
addr.svm_family = AF_VSOCK;
addr.svm_port = port;
- addr.svm_cid = VMADDR_CID_ANY;
+ addr.svm_cid = cid;
auto casted_addr = reinterpret_cast<sockaddr*>(&addr);
if (vsock->Bind(casted_addr, sizeof(addr)) == -1) {
LOG(ERROR) << "Bind failed (" << vsock->StrError() << ")";
diff --git a/common/libs/fs/shared_fd.h b/common/libs/fs/shared_fd.h
index e712ab9..114d199 100644
--- a/common/libs/fs/shared_fd.h
+++ b/common/libs/fs/shared_fd.h
@@ -141,7 +141,8 @@
static SharedFD SocketLocalServer(const std::string& name, bool is_abstract,
int in_type, mode_t mode);
static SharedFD SocketLocalServer(int port, int type);
- static SharedFD VsockServer(unsigned int port, int type);
+ static SharedFD VsockServer(unsigned int port, int type,
+ unsigned int cid = VMADDR_CID_ANY);
static SharedFD VsockServer(int type);
static SharedFD VsockClient(unsigned int cid, unsigned int port, int type);
diff --git a/common/libs/utils/Android.bp b/common/libs/utils/Android.bp
index f8ca5e2..860b684 100644
--- a/common/libs/utils/Android.bp
+++ b/common/libs/utils/Android.bp
@@ -30,18 +30,21 @@
"base64.cpp",
"tcp_socket.cpp",
"tee_logging.cpp",
+ "vsock_connection.cpp",
],
shared: {
shared_libs: [
"libbase",
"libcuttlefish_fs",
"libcrypto",
+ "libjsoncpp",
],
},
static: {
static_libs: [
"libbase",
"libcuttlefish_fs",
+ "libjsoncpp",
],
shared_libs: [
"libcrypto", // libcrypto_static is not accessible from all targets
@@ -49,3 +52,12 @@
},
defaults: ["cuttlefish_host"],
}
+
+cc_library {
+ name: "libvsock_utils",
+ srcs: ["vsock_connection.cpp"],
+ shared_libs: ["libbase", "libcuttlefish_fs", "liblog", "libjsoncpp"],
+ defaults: ["cuttlefish_guest_only"],
+ include_dirs: ["device/google/cuttlefish"],
+ export_include_dirs: ["."],
+}
diff --git a/common/libs/utils/vsock_connection.cpp b/common/libs/utils/vsock_connection.cpp
new file mode 100644
index 0000000..6142b69
--- /dev/null
+++ b/common/libs/utils/vsock_connection.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/libs/utils/vsock_connection.h"
+
+#include "common/libs/fs/shared_buf.h"
+#include "common/libs/fs/shared_select.h"
+
+#include "android-base/logging.h"
+
+namespace cuttlefish {
+
+VsockConnection::~VsockConnection() { Disconnect(); }
+
+std::future<bool> VsockConnection::ConnectAsync(unsigned int port,
+ unsigned int cid) {
+ return std::async(std::launch::async,
+ [this, port, cid]() { return Connect(port, cid); });
+}
+
+void VsockConnection::Disconnect() {
+ LOG(INFO) << "Disconnecting with fd status:" << fd_->StrError();
+ fd_->Shutdown(SHUT_RDWR);
+ if (disconnect_callback_) {
+ disconnect_callback_();
+ }
+ fd_->Close();
+}
+
+void VsockConnection::SetDisconnectCallback(std::function<void()> callback) {
+ disconnect_callback_ = callback;
+}
+
+bool VsockConnection::IsConnected() const { return fd_->IsOpen(); }
+
+bool VsockConnection::DataAvailable() const {
+ SharedFDSet read_set;
+ read_set.Set(fd_);
+ struct timeval timeout = {0, 0};
+ return Select(&read_set, nullptr, nullptr, &timeout) > 0;
+}
+
+int32_t VsockConnection::Read() {
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ int32_t result;
+ if (ReadExactBinary(fd_, &result) != sizeof(result)) {
+ Disconnect();
+ return 0;
+ }
+ return result;
+}
+
+bool VsockConnection::Read(std::vector<char>& data) {
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ return ReadExact(fd_, &data) == data.size();
+}
+
+std::vector<char> VsockConnection::Read(size_t size) {
+ if (size == 0) {
+ return {};
+ }
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ std::vector<char> result(size);
+ if (ReadExact(fd_, &result) != size) {
+ Disconnect();
+ return {};
+ }
+ return result;
+}
+
+std::future<std::vector<char>> VsockConnection::ReadAsync(size_t size) {
+ return std::async(std::launch::async, [this, size]() { return Read(size); });
+}
+
+// Message format is buffer size followed by buffer data
+std::vector<char> VsockConnection::ReadMessage() {
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ auto size = Read();
+ if (size < 0) {
+ Disconnect();
+ return {};
+ }
+ return Read(size);
+}
+
+bool VsockConnection::ReadMessage(std::vector<char>& data) {
+ std::lock_guard<std::recursive_mutex> lock(read_mutex_);
+ auto size = Read();
+ if (size < 0) {
+ Disconnect();
+ return false;
+ }
+ data.resize(size);
+ return Read(data);
+}
+
+std::future<std::vector<char>> VsockConnection::ReadMessageAsync() {
+ return std::async(std::launch::async, [this]() { return ReadMessage(); });
+}
+
+Json::Value VsockConnection::ReadJsonMessage() {
+ auto msg = ReadMessage();
+ Json::CharReaderBuilder builder;
+ Json::CharReader* reader = builder.newCharReader();
+ Json::Value json_msg;
+ std::string errors;
+ if (!reader->parse(msg.data(), msg.data() + msg.size(), &json_msg, &errors)) {
+ return {};
+ }
+ return json_msg;
+}
+
+std::future<Json::Value> VsockConnection::ReadJsonMessageAsync() {
+ return std::async(std::launch::async, [this]() { return ReadJsonMessage(); });
+}
+
+bool VsockConnection::Write(int32_t data) {
+ std::lock_guard<std::recursive_mutex> lock(write_mutex_);
+ if (WriteAllBinary(fd_, &data) != sizeof(data)) {
+ Disconnect();
+ return false;
+ }
+ return true;
+}
+
+bool VsockConnection::Write(const char* data, unsigned int size) {
+ std::lock_guard<std::recursive_mutex> lock(write_mutex_);
+ if (WriteAll(fd_, data, size) != size) {
+ Disconnect();
+ return false;
+ }
+ return true;
+}
+
+bool VsockConnection::Write(const std::vector<char>& data) {
+ return Write(data.data(), data.size());
+}
+
+// Message format is buffer size followed by buffer data
+bool VsockConnection::WriteMessage(const std::string& data) {
+ return Write(data.size()) && Write(data.c_str(), data.length());
+}
+
+bool VsockConnection::WriteMessage(const std::vector<char>& data) {
+ std::lock_guard<std::recursive_mutex> lock(write_mutex_);
+ return Write(data.size()) && Write(data);
+}
+
+bool VsockConnection::WriteMessage(const Json::Value& data) {
+ Json::StreamWriterBuilder factory;
+ std::string message_str = Json::writeString(factory, data);
+ return WriteMessage(message_str);
+}
+
+bool VsockConnection::WriteStrides(const char* data, unsigned int size,
+ unsigned int num_strides, int stride_size) {
+ const char* src = data;
+ for (unsigned int i = 0; i < num_strides; ++i, src += stride_size) {
+ if (!Write(src, size)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VsockClientConnection::Connect(unsigned int port, unsigned int cid) {
+ fd_ = SharedFD::VsockClient(cid, port, SOCK_STREAM);
+ if (!fd_->IsOpen()) {
+ LOG(ERROR) << "Failed to connect:" << fd_->StrError();
+ }
+ return fd_->IsOpen();
+}
+
+VsockServerConnection::~VsockServerConnection() { ServerShutdown(); }
+
+void VsockServerConnection::ServerShutdown() {
+ if (server_fd_->IsOpen()) {
+ LOG(INFO) << __FUNCTION__
+ << ": server fd status:" << server_fd_->StrError();
+ server_fd_->Shutdown(SHUT_RDWR);
+ server_fd_->Close();
+ }
+}
+
+bool VsockServerConnection::Connect(unsigned int port, unsigned int cid) {
+ if (!server_fd_->IsOpen()) {
+ server_fd_ = cuttlefish::SharedFD::VsockServer(port, SOCK_STREAM, cid);
+ }
+ if (server_fd_->IsOpen()) {
+ fd_ = SharedFD::Accept(*server_fd_);
+ return fd_->IsOpen();
+ } else {
+ return false;
+ }
+}
+
+} // namespace cuttlefish
diff --git a/common/libs/utils/vsock_connection.h b/common/libs/utils/vsock_connection.h
new file mode 100644
index 0000000..1a16b2f
--- /dev/null
+++ b/common/libs/utils/vsock_connection.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <json/json.h>
+#include <functional>
+#include <future>
+#include <mutex>
+#include <vector>
+#include "common/libs/fs/shared_fd.h"
+
+namespace cuttlefish {
+
+class VsockConnection {
+ public:
+ virtual ~VsockConnection();
+ virtual bool Connect(unsigned int port, unsigned int cid) = 0;
+ virtual void Disconnect();
+ std::future<bool> ConnectAsync(unsigned int port, unsigned int cid);
+ void SetDisconnectCallback(std::function<void()> callback);
+
+ bool IsConnected() const;
+ bool DataAvailable() const;
+
+ int32_t Read();
+ bool Read(std::vector<char>& data);
+ std::vector<char> Read(size_t size);
+ std::future<std::vector<char>> ReadAsync(size_t size);
+
+ bool ReadMessage(std::vector<char>& data);
+ std::vector<char> ReadMessage();
+ std::future<std::vector<char>> ReadMessageAsync();
+ Json::Value ReadJsonMessage();
+ std::future<Json::Value> ReadJsonMessageAsync();
+
+ bool Write(int32_t data);
+ bool Write(const char* data, unsigned int size);
+ bool Write(const std::vector<char>& data);
+ bool WriteMessage(const std::string& data);
+ bool WriteMessage(const std::vector<char>& data);
+ bool WriteMessage(const Json::Value& data);
+ bool WriteStrides(const char* data, unsigned int size,
+ unsigned int num_strides, int stride_size);
+
+ protected:
+ std::recursive_mutex read_mutex_;
+ std::recursive_mutex write_mutex_;
+ std::function<void()> disconnect_callback_;
+ SharedFD fd_;
+};
+
+class VsockClientConnection : public VsockConnection {
+ public:
+ bool Connect(unsigned int port, unsigned int cid) override;
+};
+
+class VsockServerConnection : public VsockConnection {
+ public:
+ virtual ~VsockServerConnection();
+ void ServerShutdown();
+ bool Connect(unsigned int port, unsigned int cid) override;
+
+ private:
+ SharedFD server_fd_;
+};
+
+} // namespace cuttlefish
diff --git a/guest/commands/bt_vhci_forwarder/Android.bp b/guest/commands/bt_vhci_forwarder/Android.bp
index 72dd7c9..a9f63b1 100644
--- a/guest/commands/bt_vhci_forwarder/Android.bp
+++ b/guest/commands/bt_vhci_forwarder/Android.bp
@@ -13,8 +13,8 @@
"liblog",
],
static_libs: [
+ "h4_packetizer_lib",
"libgflags",
- "libbt-rootcanal",
],
defaults: ["cuttlefish_guest_only"]
}
diff --git a/guest/commands/bt_vhci_forwarder/main.cpp b/guest/commands/bt_vhci_forwarder/main.cpp
index 5d5fcce..d5c4622 100644
--- a/guest/commands/bt_vhci_forwarder/main.cpp
+++ b/guest/commands/bt_vhci_forwarder/main.cpp
@@ -128,7 +128,6 @@
send(vhci_fd, HCI_ISODATA_PKT, raw_iso.data(), raw_iso.size());
},
[]() { LOG(INFO) << "HCI socket device disconnected"; });
-
while (true) {
int ret = TEMP_FAILURE_RETRY(poll(fds, 2, -1));
if (ret < 0) {
@@ -143,11 +142,15 @@
PLOG(ERROR) << "vhci to virtio-console failed";
}
}
-
+ if (fds[1].revents & POLLHUP) {
+ LOG(ERROR) << "PollHUP";
+ usleep(50 * 1000);
+ continue;
+ }
if (fds[1].revents & (POLLIN | POLLERR)) {
// 'virtio-console to vhci' depends on H4Packetizer because vhci expects
// full packet, but the data from virtio-console could be partial.
h4.OnDataReady(virtio_fd);
}
}
-}
\ No newline at end of file
+}
diff --git a/guest/commands/setup_wifi/main.cpp b/guest/commands/setup_wifi/main.cpp
index 0a07c97..032f39e 100644
--- a/guest/commands/setup_wifi/main.cpp
+++ b/guest/commands/setup_wifi/main.cpp
@@ -139,9 +139,9 @@
gflags::ParseCommandLineFlags(&argc, &argv, true);
- int renamed_eth0 = RenameNetwork("eth0", "buried_eth0");
- if (renamed_eth0 != 0) {
- return renamed_eth0;
+ int renamed_eth2 = RenameNetwork("eth2", "buried_eth2");
+ if (renamed_eth2 != 0) {
+ return renamed_eth2;
}
- return CreateWifiWrapper("buried_eth0", "wlan0");
+ return CreateWifiWrapper("buried_eth2", "wlan0");
}
diff --git a/guest/hals/audio/audio_hw.c b/guest/hals/audio/audio_hw.c
index 51110d7..c0c705a 100644
--- a/guest/hals/audio/audio_hw.c
+++ b/guest/hals/audio/audio_hw.c
@@ -738,8 +738,7 @@
static int refine_input_parameters(uint32_t *sample_rate, audio_format_t *format, audio_channel_mask_t *channel_mask)
{
- // Crosvm only supports 48kHz streams for input
- static const uint32_t sample_rates [] = {48000};
+ static const uint32_t sample_rates [] = {8000, 11025, 16000, 22050, 44100, 48000};
static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t);
bool inval = false;
// Only PCM_16_bit is supported. If this is changed, stereo to mono drop
@@ -1339,6 +1338,11 @@
return 0;
}
+static int adev_set_audio_port_config(struct audio_hw_device *dev,
+ const struct audio_port_config *config) {
+ return 0;
+}
+
static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
{
return -ENOSYS;
@@ -1783,6 +1787,7 @@
adev->device.set_parameters = adev_set_parameters; // no op
adev->device.get_parameters = adev_get_parameters; // no op
adev->device.get_audio_port = adev_get_audio_port; // no op
+ adev->device.set_audio_port_config = adev_set_audio_port_config; // no op
adev->device.get_input_buffer_size = adev_get_input_buffer_size;
adev->device.open_output_stream = adev_open_output_stream;
adev->device.close_output_stream = adev_close_output_stream;
diff --git a/guest/hals/camera/Android.bp b/guest/hals/camera/Android.bp
new file mode 100644
index 0000000..7af1542
--- /dev/null
+++ b/guest/hals/camera/Android.bp
@@ -0,0 +1,84 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+ name: "[email protected]",
+ defaults: ["hidl_defaults"],
+ proprietary: true,
+ relative_install_path: "hw",
+ srcs: ["external-service.cpp"],
+ compile_multilib: "first",
+ init_rc: ["[email protected]"],
+ shared_libs: [
+ "[email protected]",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+}
+
+cc_library_shared {
+ name: "[email protected]",
+ defaults: ["hidl_defaults"],
+ proprietary: true,
+ relative_install_path: "hw",
+ srcs: [
+ "vsock_camera_provider_2_7.cpp",
+ "vsock_camera_device_3_4.cpp",
+ "vsock_camera_device_session_3_4.cpp",
+ "vsock_camera_metadata.cpp",
+ "vsock_camera_server.cpp",
+ "vsock_frame_provider.cpp",
+ "cached_stream_buffer.cpp",
+ "stream_buffer_cache.cpp",
+ ],
+ shared_libs: [
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "[email protected]",
+ "libcamera_metadata",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "libvsock_utils",
+ "libcuttlefish_fs",
+ "libjsoncpp",
+ "libyuv",
+ "libsync",
+ "libfmq",
+ "libgralloctypes",
+ ],
+ header_libs: [
+ "[email protected]_headers",
+ "[email protected]_headers",
+ "[email protected]_headers",
+ "[email protected]_headers",
+ ],
+ static_libs: [
+ "[email protected]",
+ ],
+ include_dirs: ["device/google/cuttlefish"],
+ export_include_dirs: ["."],
+}
diff --git a/guest/hals/camera/[email protected] b/guest/hals/camera/[email protected]
new file mode 100644
index 0000000..16bde0b
--- /dev/null
+++ b/guest/hals/camera/[email protected]
@@ -0,0 +1,11 @@
+service vendor.camera-provider-2-7-ext /vendor/bin/hw/[email protected]
+ interface [email protected]::ICameraProvider external/0
+ interface [email protected]::ICameraProvider external/0
+ interface [email protected]::ICameraProvider external/0
+ interface [email protected]::ICameraProvider external/0
+ class hal
+ user cameraserver
+ group audio camera input drmrpc
+ ioprio rt 4
+ capabilities SYS_NICE
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/guest/hals/camera/cached_stream_buffer.cpp b/guest/hals/camera/cached_stream_buffer.cpp
new file mode 100644
index 0000000..ff692c7
--- /dev/null
+++ b/guest/hals/camera/cached_stream_buffer.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "CachedStreamBuffer"
+#include "cached_stream_buffer.h"
+#include <hardware/gralloc.h>
+#include <log/log.h>
+#include <sync/sync.h>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+namespace {
+HandleImporter g_importer;
+}
+
+ReleaseFence::ReleaseFence(int fence_fd) : handle_(nullptr) {
+ if (fence_fd >= 0) {
+ handle_ = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
+ handle_->data[0] = fence_fd;
+ }
+}
+
+ReleaseFence::~ReleaseFence() {
+ if (handle_ != nullptr) {
+ native_handle_close(handle_);
+ native_handle_delete(handle_);
+ }
+}
+
+CachedStreamBuffer::CachedStreamBuffer()
+ : buffer_(nullptr), buffer_id_(0), stream_id_(0), acquire_fence_(-1) {}
+
+CachedStreamBuffer::CachedStreamBuffer(const StreamBuffer& buffer)
+ : buffer_(buffer.buffer.getNativeHandle()),
+ buffer_id_(buffer.bufferId),
+ stream_id_(buffer.streamId),
+ acquire_fence_(-1) {
+ g_importer.importBuffer(buffer_);
+ g_importer.importFence(buffer.acquireFence, acquire_fence_);
+}
+
+CachedStreamBuffer::CachedStreamBuffer(CachedStreamBuffer&& from) noexcept {
+ buffer_ = from.buffer_;
+ buffer_id_ = from.buffer_id_;
+ stream_id_ = from.stream_id_;
+ acquire_fence_ = from.acquire_fence_;
+ from.acquire_fence_ = -1;
+ from.buffer_ = nullptr;
+}
+
+CachedStreamBuffer& CachedStreamBuffer::operator=(
+ CachedStreamBuffer&& from) noexcept {
+ if (this != &from) {
+ buffer_ = from.buffer_;
+ buffer_id_ = from.buffer_id_;
+ stream_id_ = from.stream_id_;
+ acquire_fence_ = from.acquire_fence_;
+ from.acquire_fence_ = -1;
+ from.buffer_ = nullptr;
+ }
+ return *this;
+}
+
+CachedStreamBuffer::~CachedStreamBuffer() {
+ if (buffer_ != nullptr) {
+ g_importer.freeBuffer(buffer_);
+ }
+ g_importer.closeFence(acquire_fence_);
+}
+
+void CachedStreamBuffer::importFence(const native_handle_t* fence_handle) {
+ g_importer.closeFence(acquire_fence_);
+ g_importer.importFence(fence_handle, acquire_fence_);
+}
+
+YCbCrLayout CachedStreamBuffer::acquireAsYUV(int32_t width, int32_t height,
+ int timeout_ms) {
+ if (acquire_fence_ >= 0) {
+ if (sync_wait(acquire_fence_, timeout_ms)) {
+ ALOGW("%s: timeout while waiting acquire fence", __FUNCTION__);
+ return {};
+ } else {
+ ::close(acquire_fence_);
+ acquire_fence_ = -1;
+ }
+ }
+ IMapper::Rect region{0, 0, width, height};
+ return g_importer.lockYCbCr(buffer_, GRALLOC_USAGE_SW_WRITE_OFTEN, region);
+}
+
+void* CachedStreamBuffer::acquireAsBlob(int32_t size, int timeout_ms) {
+ if (acquire_fence_ >= 0) {
+ if (sync_wait(acquire_fence_, timeout_ms)) {
+ return nullptr;
+ } else {
+ ::close(acquire_fence_);
+ acquire_fence_ = -1;
+ }
+ }
+ return g_importer.lock(buffer_, GRALLOC_USAGE_SW_WRITE_OFTEN, size);
+}
+
+int CachedStreamBuffer::release() { return g_importer.unlock(buffer_); }
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/cached_stream_buffer.h b/guest/hals/camera/cached_stream_buffer.h
new file mode 100644
index 0000000..09a4047
--- /dev/null
+++ b/guest/hals/camera/cached_stream_buffer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include "HandleImporter.h"
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::device::V3_2::StreamBuffer;
+
+// Small wrapper for allocating/freeing native handles
+class ReleaseFence {
+ public:
+ ReleaseFence(int fence_fd);
+ ~ReleaseFence();
+
+ native_handle_t* handle() const { return handle_; }
+
+ private:
+ native_handle_t* handle_;
+};
+
+// CachedStreamBuffer holds a buffer of camera3 stream.
+class CachedStreamBuffer {
+ public:
+ CachedStreamBuffer();
+ CachedStreamBuffer(const StreamBuffer& buffer);
+ // Not copyable
+ CachedStreamBuffer(const CachedStreamBuffer&) = delete;
+ CachedStreamBuffer& operator=(const CachedStreamBuffer&) = delete;
+ // ...but movable
+ CachedStreamBuffer(CachedStreamBuffer&& from) noexcept;
+ CachedStreamBuffer& operator=(CachedStreamBuffer&& from) noexcept;
+
+ ~CachedStreamBuffer();
+
+ bool valid() const { return buffer_ != nullptr; }
+ uint64_t bufferId() const { return buffer_id_; }
+ int32_t streamId() const { return stream_id_; }
+ int acquireFence() const { return acquire_fence_; }
+
+ void importFence(const native_handle_t* fence_handle);
+ // Acquire methods wait first on acquire fence and then return pointers to
+ // data. Data is nullptr if the wait timed out
+ YCbCrLayout acquireAsYUV(int32_t width, int32_t height, int timeout_ms);
+ void* acquireAsBlob(int32_t size, int timeout_ms);
+ int release();
+
+ private:
+ buffer_handle_t buffer_;
+ uint64_t buffer_id_;
+ int32_t stream_id_;
+ int acquire_fence_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/external-service.cpp b/guest/hals/camera/external-service.cpp
new file mode 100644
index 0000000..22e86a6
--- /dev/null
+++ b/guest/hals/camera/external-service.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "[email protected]"
+
+#include <android/hardware/camera/provider/2.7/ICameraProvider.h>
+#include <hidl/LegacySupport.h>
+
+#include <binder/ProcessState.h>
+
+using android::hardware::defaultPassthroughServiceImplementation;
+using android::hardware::camera::provider::V2_7::ICameraProvider;
+
+int main() {
+ ALOGI("External camera provider service is starting.");
+ // The camera HAL may communicate to other vendor components via
+ // /dev/vndbinder
+ android::ProcessState::initWithDriver("/dev/vndbinder");
+ return defaultPassthroughServiceImplementation<ICameraProvider>(
+ "external/0", /*maxThreads*/ 6);
+}
diff --git a/guest/hals/camera/manifest.xml b/guest/hals/camera/manifest.xml
new file mode 100644
index 0000000..4f23875
--- /dev/null
+++ b/guest/hals/camera/manifest.xml
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.camera.provider</name>
+ <transport>hwbinder</transport>
+ <version>2.7</version>
+ <interface>
+ <name>ICameraProvider</name>
+ <instance>external/0</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/guest/hals/camera/stream_buffer_cache.cpp b/guest/hals/camera/stream_buffer_cache.cpp
new file mode 100644
index 0000000..dfa13cb
--- /dev/null
+++ b/guest/hals/camera/stream_buffer_cache.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "stream_buffer_cache.h"
+#include <algorithm>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+std::shared_ptr<CachedStreamBuffer> StreamBufferCache::get(uint64_t buffer_id) {
+ auto id_match =
+ [buffer_id](const std::shared_ptr<CachedStreamBuffer>& buffer) {
+ return buffer->bufferId() == buffer_id;
+ };
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto found = std::find_if(cache_.begin(), cache_.end(), id_match);
+ return (found != cache_.end()) ? *found : nullptr;
+}
+
+void StreamBufferCache::remove(uint64_t buffer_id) {
+ auto id_match =
+ [&buffer_id](const std::shared_ptr<CachedStreamBuffer>& buffer) {
+ return buffer->bufferId() == buffer_id;
+ };
+ std::lock_guard<std::mutex> lock(mutex_);
+ cache_.erase(std::remove_if(cache_.begin(), cache_.end(), id_match));
+}
+
+void StreamBufferCache::update(const StreamBuffer& buffer) {
+ auto id = buffer.bufferId;
+ auto id_match = [id](const std::shared_ptr<CachedStreamBuffer>& buffer) {
+ return buffer->bufferId() == id;
+ };
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto found = std::find_if(cache_.begin(), cache_.end(), id_match);
+ if (found == cache_.end()) {
+ cache_.emplace_back(std::make_shared<CachedStreamBuffer>(buffer));
+ } else {
+ (*found)->importFence(buffer.acquireFence);
+ }
+}
+
+void StreamBufferCache::clear() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ cache_.clear();
+}
+
+void StreamBufferCache::removeStreamsExcept(std::set<int32_t> streams_to_keep) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ for (auto it = cache_.begin(); it != cache_.end();) {
+ if (streams_to_keep.count((*it)->streamId()) == 0) {
+ it = cache_.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/stream_buffer_cache.h b/guest/hals/camera/stream_buffer_cache.h
new file mode 100644
index 0000000..af57bc4
--- /dev/null
+++ b/guest/hals/camera/stream_buffer_cache.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <mutex>
+#include <set>
+#include <vector>
+#include "cached_stream_buffer.h"
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+class StreamBufferCache {
+ public:
+ std::shared_ptr<CachedStreamBuffer> get(uint64_t buffer_id);
+ void remove(uint64_t buffer_id);
+ void update(const StreamBuffer& buffer);
+ void clear();
+ void removeStreamsExcept(std::set<int32_t> streams_to_keep = {});
+
+ private:
+ std::mutex mutex_;
+ std::vector<std::shared_ptr<CachedStreamBuffer>> cache_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_device_3_4.cpp b/guest/hals/camera/vsock_camera_device_3_4.cpp
new file mode 100644
index 0000000..310a9f8
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_device_3_4.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VsockCameraDevice"
+//#define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include <algorithm>
+#include <array>
+#include "CameraMetadata.h"
+#include "android-base/macros.h"
+#include "include/convert.h"
+#include "vsock_camera_device_3_4.h"
+#include "vsock_camera_device_session_3_4.h"
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+VsockCameraDevice::VsockCameraDevice(
+ const std::string& id, const Settings& settings,
+ std::shared_ptr<cuttlefish::VsockConnection> connection)
+ : id_(id),
+ metadata_(settings.width, settings.height, settings.frame_rate),
+ connection_(connection),
+ is_open_(false) {
+ ALOGI("%s", __FUNCTION__);
+}
+
+VsockCameraDevice::~VsockCameraDevice() { ALOGI("%s", __FUNCTION__); }
+
+Return<void> VsockCameraDevice::getResourceCost(
+ ICameraDevice::getResourceCost_cb _hidl_cb) {
+ CameraResourceCost resCost;
+ resCost.resourceCost = 100;
+ _hidl_cb(Status::OK, resCost);
+ return Void();
+}
+
+Return<void> VsockCameraDevice::getCameraCharacteristics(
+ ICameraDevice::getCameraCharacteristics_cb _hidl_cb) {
+ V3_2::CameraMetadata hidl_vec;
+ const camera_metadata_t* metadata_ptr = metadata_.getAndLock();
+ V3_2::implementation::convertToHidl(metadata_ptr, &hidl_vec);
+ _hidl_cb(Status::OK, hidl_vec);
+ metadata_.unlock(metadata_ptr);
+ return Void();
+}
+
+Return<Status> VsockCameraDevice::setTorchMode(TorchMode) {
+ return Status::OPERATION_NOT_SUPPORTED;
+}
+
+Return<void> VsockCameraDevice::open(const sp<ICameraDeviceCallback>& callback,
+ ICameraDevice::open_cb _hidl_cb) {
+ if (callback == nullptr) {
+ ALOGE("%s: cannot open camera %s. callback is null!", __FUNCTION__,
+ id_.c_str());
+ _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+ return Void();
+ }
+
+ bool was_open = is_open_.exchange(true);
+
+ if (was_open) {
+ ALOGE("%s: cannot open an already opened camera!", __FUNCTION__);
+ _hidl_cb(Status::CAMERA_IN_USE, nullptr);
+ return Void();
+ }
+ ALOGI("%s: Initializing device for camera %s", __FUNCTION__, id_.c_str());
+ frame_provider_ = std::make_shared<cuttlefish::VsockFrameProvider>();
+ frame_provider_->start(connection_, metadata_.getPreferredWidth(),
+ metadata_.getPreferredHeight());
+ session_ = new VsockCameraDeviceSession(metadata_, frame_provider_, callback);
+ _hidl_cb(Status::OK, session_);
+ return Void();
+}
+
+Return<void> VsockCameraDevice::dumpState(
+ const ::android::hardware::hidl_handle& handle) {
+ if (handle.getNativeHandle() == nullptr) {
+ ALOGE("%s: handle must not be null", __FUNCTION__);
+ return Void();
+ }
+ if (handle->numFds != 1 || handle->numInts != 0) {
+ ALOGE("%s: handle must contain 1 FD and 0 integers! Got %d FDs and %d ints",
+ __FUNCTION__, handle->numFds, handle->numInts);
+ return Void();
+ }
+ int fd = handle->data[0];
+ dprintf(fd, "Camera:%s\n", id_.c_str());
+ return Void();
+}
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_device_3_4.h b/guest/hals/camera/vsock_camera_device_3_4.h
new file mode 100644
index 0000000..9d92991
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_device_3_4.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "CameraMetadata.h"
+
+#include <android/hardware/camera/device/3.2/ICameraDevice.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "vsock_camera_device_session_3_4.h"
+#include "vsock_camera_metadata.h"
+#include "vsock_connection.h"
+#include "vsock_frame_provider.h"
+
+#include <vector>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+using namespace ::android::hardware::camera::device;
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::camera::common::V1_0::CameraResourceCost;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::TorchMode;
+using ::android::hardware::camera::device::V3_2::ICameraDevice;
+using ::android::hardware::camera::device::V3_2::ICameraDeviceCallback;
+
+class VsockCameraDevice : public ICameraDevice {
+ public:
+ using Settings = struct {
+ int32_t width;
+ int32_t height;
+ double frame_rate;
+ };
+
+ VsockCameraDevice(const std::string& id, const Settings& settings,
+ std::shared_ptr<cuttlefish::VsockConnection> connection);
+ virtual ~VsockCameraDevice();
+
+ /* Methods from ::android::hardware::camera::device::V3_2::ICameraDevice
+ * follow. */
+ Return<void> getResourceCost(ICameraDevice::getResourceCost_cb _hidl_cb);
+ Return<void> getCameraCharacteristics(
+ ICameraDevice::getCameraCharacteristics_cb _hidl_cb);
+ Return<Status> setTorchMode(TorchMode);
+ Return<void> open(const sp<ICameraDeviceCallback>&, ICameraDevice::open_cb);
+ Return<void> dumpState(const ::android::hardware::hidl_handle&);
+ /* End of Methods from
+ * ::android::hardware::camera::device::V3_2::ICameraDevice */
+
+ private:
+ std::string id_;
+ VsockCameraMetadata metadata_;
+ std::shared_ptr<cuttlefish::VsockConnection> connection_;
+ std::shared_ptr<cuttlefish::VsockFrameProvider> frame_provider_;
+ std::atomic<bool> is_open_;
+ sp<VsockCameraDeviceSession> session_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_device_session_3_4.cpp b/guest/hals/camera/vsock_camera_device_session_3_4.cpp
new file mode 100644
index 0000000..9c0b597
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_device_session_3_4.cpp
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "VsockCameraDeviceSession"
+#include "vsock_camera_device_session_3_4.h"
+#include <hidl/Status.h>
+#include <include/convert.h>
+#include <inttypes.h>
+#include <libyuv.h>
+#include <log/log.h>
+#include "vsock_camera_metadata.h"
+
+// Partially copied from ExternalCameraDeviceSession
+namespace android::hardware::camera::device::V3_4::implementation {
+
+VsockCameraDeviceSession::VsockCameraDeviceSession(
+ VsockCameraMetadata camera_characteristics,
+ std::shared_ptr<cuttlefish::VsockFrameProvider> frame_provider,
+ const sp<ICameraDeviceCallback>& callback)
+ : camera_characteristics_(camera_characteristics),
+ frame_provider_(frame_provider),
+ callback_(callback) {
+ static constexpr size_t kMsgQueueSize = 256 * 1024;
+ request_queue_ =
+ std::make_unique<MessageQueue<uint8_t, kSynchronizedReadWrite>>(
+ kMsgQueueSize, false);
+ result_queue_ =
+ std::make_shared<MessageQueue<uint8_t, kSynchronizedReadWrite>>(
+ kMsgQueueSize, false);
+ unsigned int timeout_ms = 1000 / camera_characteristics.getPreferredFps();
+ process_requests_ = true;
+ request_processor_ =
+ std::thread([this, timeout_ms] { processRequestLoop(timeout_ms); });
+}
+
+VsockCameraDeviceSession::~VsockCameraDeviceSession() { close(); }
+
+Return<void> VsockCameraDeviceSession::constructDefaultRequestSettings(
+ V3_2::RequestTemplate type,
+ V3_2::ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) {
+ auto frame_rate = camera_characteristics_.getPreferredFps();
+ auto metadata = VsockCameraRequestMetadata(frame_rate, type);
+ V3_2::CameraMetadata hidl_metadata;
+ Status status = metadata.isValid() ? Status::OK : Status::ILLEGAL_ARGUMENT;
+ if (metadata.isValid()) {
+ camera_metadata_t* metadata_ptr = metadata.release();
+ hidl_metadata.setToExternal((uint8_t*)metadata_ptr,
+ get_camera_metadata_size(metadata_ptr));
+ }
+ _hidl_cb(status, hidl_metadata);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::getCaptureRequestMetadataQueue(
+ ICameraDeviceSession::getCaptureRequestMetadataQueue_cb _hidl_cb) {
+ _hidl_cb(*request_queue_->getDesc());
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::getCaptureResultMetadataQueue(
+ ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) {
+ _hidl_cb(*result_queue_->getDesc());
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::configureStreams(
+ const V3_2::StreamConfiguration& streams,
+ ICameraDeviceSession::configureStreams_cb _hidl_cb) {
+ // common configureStreams operate with v3_2 config and v3_3
+ // streams so we need to "downcast" v3_3 streams to v3_2 streams
+ V3_2::HalStreamConfiguration out_v32;
+ V3_3::HalStreamConfiguration out_v33;
+
+ Status status = configureStreams(streams, &out_v33);
+ size_t size = out_v33.streams.size();
+ out_v32.streams.resize(size);
+ for (size_t i = 0; i < size; i++) {
+ out_v32.streams[i] = out_v33.streams[i].v3_2;
+ }
+ _hidl_cb(status, out_v32);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::configureStreams_3_3(
+ const V3_2::StreamConfiguration& streams,
+ ICameraDeviceSession::configureStreams_3_3_cb _hidl_cb) {
+ V3_3::HalStreamConfiguration out_v33;
+ Status status = configureStreams(streams, &out_v33);
+ _hidl_cb(status, out_v33);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::configureStreams_3_4(
+ const V3_4::StreamConfiguration& requestedConfiguration,
+ ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb) {
+ // common configureStreams operate with v3_2 config and v3_3
+ // streams so we need to "downcast" v3_4 config to v3_2 and
+ // "upcast" v3_3 streams to v3_4 streams
+ V3_2::StreamConfiguration config_v32;
+ V3_3::HalStreamConfiguration out_v33;
+ V3_4::HalStreamConfiguration out_v34;
+
+ config_v32.operationMode = requestedConfiguration.operationMode;
+ config_v32.streams.resize(requestedConfiguration.streams.size());
+ for (size_t i = 0; i < config_v32.streams.size(); i++) {
+ config_v32.streams[i] = requestedConfiguration.streams[i].v3_2;
+ }
+ max_blob_size_ = getBlobSize(requestedConfiguration);
+ Status status = configureStreams(config_v32, &out_v33);
+
+ out_v34.streams.resize(out_v33.streams.size());
+ for (size_t i = 0; i < out_v34.streams.size(); i++) {
+ out_v34.streams[i].v3_3 = out_v33.streams[i];
+ }
+ _hidl_cb(status, out_v34);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::processCaptureRequest(
+ const hidl_vec<CaptureRequest>& requests,
+ const hidl_vec<BufferCache>& cachesToRemove,
+ ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) {
+ updateBufferCaches(cachesToRemove);
+
+ uint32_t count;
+ Status s = Status::OK;
+ for (count = 0; count < requests.size(); count++) {
+ s = processOneCaptureRequest(requests[count]);
+ if (s != Status::OK) {
+ break;
+ }
+ }
+
+ _hidl_cb(s, count);
+ return Void();
+}
+
+Return<void> VsockCameraDeviceSession::processCaptureRequest_3_4(
+ const hidl_vec<V3_4::CaptureRequest>& requests,
+ const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+ ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) {
+ updateBufferCaches(cachesToRemove);
+
+ uint32_t count;
+ Status s = Status::OK;
+ for (count = 0; count < requests.size(); count++) {
+ s = processOneCaptureRequest(requests[count].v3_2);
+ if (s != Status::OK) {
+ break;
+ }
+ }
+
+ _hidl_cb(s, count);
+ return Void();
+}
+
+Return<Status> VsockCameraDeviceSession::flush() {
+ auto timeout = std::chrono::seconds(1);
+ std::unique_lock<std::mutex> lock(request_mutex_);
+ flushing_requests_ = true;
+ auto is_empty = [this] { return pending_requests_.empty(); };
+ if (!queue_empty_.wait_for(lock, timeout, is_empty)) {
+ ALOGE("Flush timeout - %zu pending requests", pending_requests_.size());
+ }
+ flushing_requests_ = false;
+ return Status::OK;
+}
+
+Return<void> VsockCameraDeviceSession::close() {
+ process_requests_ = false;
+ if (request_processor_.joinable()) {
+ request_processor_.join();
+ }
+ frame_provider_->stop();
+ buffer_cache_.clear();
+ ALOGI("%s", __FUNCTION__);
+ return Void();
+}
+
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+Status VsockCameraDeviceSession::configureStreams(
+ const V3_2::StreamConfiguration& config,
+ V3_3::HalStreamConfiguration* out) {
+ Status status = isStreamConfigurationSupported(config);
+ if (status != Status::OK) {
+ return status;
+ }
+ updateStreamInfo(config);
+ out->streams.resize(config.streams.size());
+ for (size_t i = 0; i < config.streams.size(); i++) {
+ out->streams[i].overrideDataSpace = config.streams[i].dataSpace;
+ out->streams[i].v3_2.id = config.streams[i].id;
+ out->streams[i].v3_2.producerUsage =
+ config.streams[i].usage | BufferUsage::CPU_WRITE_OFTEN;
+ out->streams[i].v3_2.consumerUsage = 0;
+ out->streams[i].v3_2.maxBuffers = 2;
+ out->streams[i].v3_2.overrideFormat =
+ config.streams[i].format == PixelFormat::IMPLEMENTATION_DEFINED
+ ? PixelFormat::YCBCR_420_888
+ : config.streams[i].format;
+ }
+ return Status::OK;
+}
+
+using ::android::hardware::camera::device::V3_2::StreamRotation;
+using ::android::hardware::camera::device::V3_2::StreamType;
+Status VsockCameraDeviceSession::isStreamConfigurationSupported(
+ const V3_2::StreamConfiguration& config) {
+ camera_metadata_entry device_supported_streams = camera_characteristics_.find(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ int32_t stall_stream_count = 0;
+ int32_t stream_count = 0;
+ for (const auto& stream : config.streams) {
+ if (stream.rotation != StreamRotation::ROTATION_0) {
+ ALOGE("Unsupported rotation enum value %d", stream.rotation);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ if (stream.streamType == StreamType::INPUT) {
+ ALOGE("Input stream not supported");
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ bool is_supported = false;
+ // check pixel format and dimensions against camera metadata
+ for (int i = 0; i + 4 <= device_supported_streams.count; i += 4) {
+ auto format =
+ static_cast<PixelFormat>(device_supported_streams.data.i32[i]);
+ int32_t width = device_supported_streams.data.i32[i + 1];
+ int32_t height = device_supported_streams.data.i32[i + 2];
+ if (stream.format == format && stream.width == width &&
+ stream.height == height) {
+ is_supported = true;
+ break;
+ }
+ }
+ if (!is_supported) {
+ ALOGE("Unsupported format %d (%dx%d)", stream.format, stream.width,
+ stream.height);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ if (stream.format == PixelFormat::BLOB) {
+ stall_stream_count++;
+ } else {
+ stream_count++;
+ }
+ }
+ camera_metadata_entry device_stream_counts =
+ camera_characteristics_.find(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS);
+ static constexpr auto stream_index = 1;
+ auto expected_stream_count = device_stream_counts.count > stream_index
+ ? device_stream_counts.data.i32[stream_index]
+ : 0;
+ if (stream_count > expected_stream_count) {
+ ALOGE("Too many processed streams (expect <= %d, got %d)",
+ expected_stream_count, stream_count);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ static constexpr auto stall_stream_index = 2;
+ expected_stream_count =
+ device_stream_counts.count > stall_stream_index
+ ? device_stream_counts.data.i32[stall_stream_index]
+ : 0;
+ if (stall_stream_count > expected_stream_count) {
+ ALOGE("Too many stall streams (expect <= %d, got %d)",
+ expected_stream_count, stall_stream_count);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ return Status::OK;
+}
+
+unsigned int VsockCameraDeviceSession::getBlobSize(
+ const V3_4::StreamConfiguration& requestedConfiguration) {
+ camera_metadata_entry jpeg_entry =
+ camera_characteristics_.find(ANDROID_JPEG_MAX_SIZE);
+ unsigned int blob_size = jpeg_entry.count > 0 ? jpeg_entry.data.i32[0] : 0;
+ for (auto& stream : requestedConfiguration.streams) {
+ if (stream.v3_2.format == PixelFormat::BLOB) {
+ if (stream.bufferSize < blob_size) {
+ blob_size = stream.bufferSize;
+ }
+ }
+ }
+ return blob_size;
+}
+
+void VsockCameraDeviceSession::updateBufferCaches(
+ const hidl_vec<BufferCache>& to_remove) {
+ for (auto& cache : to_remove) {
+ buffer_cache_.remove(cache.bufferId);
+ }
+}
+
+void VsockCameraDeviceSession::updateStreamInfo(
+ const V3_2::StreamConfiguration& config) {
+ std::set<int32_t> stream_ids;
+ for (const auto& stream : config.streams) {
+ stream_cache_[stream.id] = stream;
+ stream_ids.emplace(stream.id);
+ }
+ buffer_cache_.removeStreamsExcept(stream_ids);
+}
+
+Status VsockCameraDeviceSession::processOneCaptureRequest(
+ const CaptureRequest& request) {
+ const camera_metadata_t* request_settings = nullptr;
+ V3_2::CameraMetadata hidl_settings;
+ if (request.fmqSettingsSize > 0) {
+ if (!getRequestSettingsFmq(request.fmqSettingsSize, hidl_settings)) {
+ ALOGE("%s: Could not read capture request settings from fmq!",
+ __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ } else if (!V3_2::implementation::convertFromHidl(hidl_settings,
+ &request_settings)) {
+ ALOGE("%s: fmq request settings metadata is corrupt!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ } else if (!V3_2::implementation::convertFromHidl(request.settings,
+ &request_settings)) {
+ ALOGE("%s: request settings metadata is corrupt!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ if (request_settings != nullptr) {
+ // Update request settings. This must happen on first request
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ latest_request_settings_ = request_settings;
+ } else if (latest_request_settings_.isEmpty()) {
+ ALOGE("%s: Undefined capture request settings!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ std::vector<uint64_t> buffer_ids;
+ for (size_t i = 0; i < request.outputBuffers.size(); i++) {
+ buffer_cache_.update(request.outputBuffers[i]);
+ buffer_ids.emplace_back(request.outputBuffers[i].bufferId);
+ }
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ ReadVsockRequest request_to_process = {
+ .buffer_ids = buffer_ids,
+ .frame_number = request.frameNumber,
+ .timestamp = 0,
+ .settings = latest_request_settings_,
+ .buffer_count = static_cast<uint32_t>(buffer_ids.size())};
+ putRequestToQueue(request_to_process);
+ return Status::OK;
+}
+
+bool VsockCameraDeviceSession::getRequestSettingsFmq(
+ uint64_t size, V3_2::CameraMetadata& hidl_settings) {
+ hidl_settings.resize(size);
+ return request_queue_->read(hidl_settings.data(), size);
+}
+
+bool VsockCameraDeviceSession::getRequestFromQueue(ReadVsockRequest& req,
+ unsigned int timeout_ms) {
+ auto timeout = std::chrono::milliseconds(timeout_ms);
+ std::unique_lock<std::mutex> lock(request_mutex_);
+ auto not_empty = [this] { return !pending_requests_.empty(); };
+ if (request_available_.wait_for(lock, timeout, not_empty)) {
+ req = pending_requests_.top();
+ pending_requests_.pop();
+ return true;
+ }
+ queue_empty_.notify_one();
+ return false;
+}
+
+void VsockCameraDeviceSession::putRequestToQueue(
+ const ReadVsockRequest& request) {
+ std::lock_guard<std::mutex> lock(request_mutex_);
+ pending_requests_.push(request);
+ request_available_.notify_one();
+}
+
+void VsockCameraDeviceSession::fillCaptureResult(
+ common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp) {
+ const uint8_t af_state = ANDROID_CONTROL_AF_STATE_INACTIVE;
+ md.update(ANDROID_CONTROL_AF_STATE, &af_state, 1);
+
+ const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
+ md.update(ANDROID_CONTROL_AE_STATE, &aeState, 1);
+
+ const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
+ md.update(ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
+
+ const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED;
+ md.update(ANDROID_CONTROL_AWB_STATE, &awbState, 1);
+
+ const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+ md.update(ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
+
+ const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
+ md.update(ANDROID_FLASH_STATE, &flashState, 1);
+
+ const uint8_t requestPipelineMaxDepth = 4;
+ md.update(ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
+
+ camera_metadata_entry active_array_size =
+ camera_characteristics_.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ md.update(ANDROID_SCALER_CROP_REGION, active_array_size.data.i32, 4);
+
+ md.update(ANDROID_SENSOR_TIMESTAMP, ×tamp, 1);
+
+ const uint8_t lensShadingMapMode =
+ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+ md.update(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
+
+ const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
+ md.update(ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1);
+}
+
+using ::android::hardware::camera::device::V3_2::MsgType;
+using ::android::hardware::camera::device::V3_2::NotifyMsg;
+void VsockCameraDeviceSession::notifyShutter(uint32_t frame_number,
+ nsecs_t timestamp) {
+ NotifyMsg msg;
+ msg.type = MsgType::SHUTTER;
+ msg.msg.shutter.frameNumber = frame_number;
+ msg.msg.shutter.timestamp = timestamp;
+ callback_->notify({msg});
+}
+
+void VsockCameraDeviceSession::notifyError(uint32_t frame_number,
+ int32_t stream_id, ErrorCode code) {
+ NotifyMsg msg;
+ msg.type = MsgType::ERROR;
+ msg.msg.error.frameNumber = frame_number;
+ msg.msg.error.errorStreamId = stream_id;
+ msg.msg.error.errorCode = code;
+ callback_->notify({msg});
+}
+
+void VsockCameraDeviceSession::tryWriteFmqResult(V3_2::CaptureResult& result) {
+ result.fmqResultSize = 0;
+ if (result_queue_->availableToWrite() == 0 || result.result.size() == 0) {
+ return;
+ }
+ if (result_queue_->write(result.result.data(), result.result.size())) {
+ result.fmqResultSize = result.result.size();
+ result.result.resize(0);
+ }
+}
+
+using ::android::hardware::camera::device::V3_2::BufferStatus;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+void VsockCameraDeviceSession::processRequestLoop(
+ unsigned int wait_timeout_ms) {
+ while (process_requests_.load()) {
+ ReadVsockRequest request;
+ if (!getRequestFromQueue(request, wait_timeout_ms)) {
+ continue;
+ }
+ if (!frame_provider_->isRunning()) {
+ notifyError(request.frame_number, -1, ErrorCode::ERROR_DEVICE);
+ break;
+ }
+ frame_provider_->waitYUVFrame(wait_timeout_ms);
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (request.timestamp == 0) {
+ request.timestamp = now;
+ notifyShutter(request.frame_number, request.timestamp);
+ }
+ std::vector<ReleaseFence> release_fences;
+ std::vector<StreamBuffer> result_buffers;
+ std::vector<uint64_t> pending_buffers;
+ bool request_ok = true;
+ for (auto buffer_id : request.buffer_ids) {
+ auto buffer = buffer_cache_.get(buffer_id);
+ auto stream_id = buffer ? buffer->streamId() : -1;
+ if (!buffer || stream_cache_.count(stream_id) == 0) {
+ ALOGE("%s: Invalid buffer", __FUNCTION__);
+ notifyError(request.frame_number, -1, ErrorCode::ERROR_REQUEST);
+ request_ok = false;
+ break;
+ }
+ bool has_result = false;
+ auto stream = stream_cache_[stream_id];
+ if (flushing_requests_.load()) {
+ has_result = false;
+ release_fences.emplace_back(buffer->acquireFence());
+ } else if (stream.format == PixelFormat::YCBCR_420_888 ||
+ stream.format == PixelFormat::IMPLEMENTATION_DEFINED) {
+ auto dst_yuv =
+ buffer->acquireAsYUV(stream.width, stream.height, wait_timeout_ms);
+ has_result =
+ frame_provider_->copyYUVFrame(stream.width, stream.height, dst_yuv);
+ release_fences.emplace_back(buffer->release());
+ } else if (stream.format == PixelFormat::BLOB) {
+ auto time_elapsed = now - request.timestamp;
+ if (time_elapsed == 0) {
+ frame_provider_->requestJpeg();
+ pending_buffers.push_back(buffer_id);
+ continue;
+ } else if (frame_provider_->jpegPending()) {
+ static constexpr auto kMaxWaitNs = 2000000000L;
+ if (time_elapsed < kMaxWaitNs) {
+ pending_buffers.push_back(buffer_id);
+ continue;
+ }
+ ALOGE("%s: Blob request timed out after %" PRId64 "ms", __FUNCTION__,
+ ns2ms(time_elapsed));
+ frame_provider_->cancelJpegRequest();
+ has_result = false;
+ release_fences.emplace_back(buffer->acquireFence());
+ notifyError(request.frame_number, buffer->streamId(),
+ ErrorCode::ERROR_BUFFER);
+ } else {
+ ALOGI("%s: Blob ready - capture duration=%" PRId64 "ms", __FUNCTION__,
+ ns2ms(time_elapsed));
+ auto dst_blob =
+ buffer->acquireAsBlob(max_blob_size_, wait_timeout_ms);
+ has_result = frame_provider_->copyJpegData(max_blob_size_, dst_blob);
+ release_fences.emplace_back(buffer->release());
+ }
+ } else {
+ ALOGE("%s: Format %d not supported", __FUNCTION__, stream.format);
+ has_result = false;
+ release_fences.emplace_back(buffer->acquireFence());
+ notifyError(request.frame_number, buffer->streamId(),
+ ErrorCode::ERROR_BUFFER);
+ }
+ result_buffers.push_back(
+ {.streamId = buffer->streamId(),
+ .bufferId = buffer->bufferId(),
+ .buffer = nullptr,
+ .status = has_result ? BufferStatus::OK : BufferStatus::ERROR,
+ .releaseFence = release_fences.back().handle()});
+ }
+ if (!request_ok) {
+ continue;
+ }
+
+ V3_2::CaptureResult result;
+ bool results_filled = request.settings.exists(ANDROID_SENSOR_TIMESTAMP);
+ if (!results_filled) {
+ fillCaptureResult(request.settings, request.timestamp);
+ const camera_metadata_t* metadata = request.settings.getAndLock();
+ V3_2::implementation::convertToHidl(metadata, &result.result);
+ request.settings.unlock(metadata);
+ tryWriteFmqResult(result);
+ }
+ if (!result_buffers.empty() || !results_filled) {
+ result.frameNumber = request.frame_number;
+ result.partialResult = !results_filled ? 1 : 0;
+ result.inputBuffer.streamId = -1;
+ result.outputBuffers = result_buffers;
+ std::vector<V3_2::CaptureResult> results{result};
+ auto status = callback_->processCaptureResult(results);
+ release_fences.clear();
+ if (!status.isOk()) {
+ ALOGE("%s: processCaptureResult error: %s", __FUNCTION__,
+ status.description().c_str());
+ }
+ }
+ if (!pending_buffers.empty()) {
+ // some buffers still pending
+ request.buffer_ids = pending_buffers;
+ putRequestToQueue(request);
+ }
+ }
+}
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_device_session_3_4.h b/guest/hals/camera/vsock_camera_device_session_3_4.h
new file mode 100644
index 0000000..2bafa75
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_device_session_3_4.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <android/hardware/camera/device/3.2/ICameraDevice.h>
+#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <fmq/MessageQueue.h>
+#include <queue>
+#include <thread>
+#include "stream_buffer_cache.h"
+#include "vsock_camera_metadata.h"
+#include "vsock_frame_provider.h"
+
+namespace android::hardware::camera::device::V3_4::implementation {
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::device::V3_2::BufferCache;
+using ::android::hardware::camera::device::V3_2::CaptureRequest;
+using ::android::hardware::camera::device::V3_2::ErrorCode;
+using ::android::hardware::camera::device::V3_2::ICameraDeviceCallback;
+using ::android::hardware::camera::device::V3_2::RequestTemplate;
+using ::android::hardware::camera::device::V3_2::Stream;
+using ::android::hardware::camera::device::V3_4::ICameraDeviceSession;
+using ::android::hardware::camera::device::V3_4::StreamConfiguration;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+
+class VsockCameraDeviceSession : public ICameraDeviceSession {
+ public:
+ VsockCameraDeviceSession(
+ VsockCameraMetadata camera_characteristics,
+ std::shared_ptr<cuttlefish::VsockFrameProvider> frame_provider,
+ const sp<ICameraDeviceCallback>& callback);
+
+ ~VsockCameraDeviceSession();
+
+ Return<void> constructDefaultRequestSettings(
+ RequestTemplate,
+ ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb);
+
+ Return<void> configureStreams(const V3_2::StreamConfiguration&,
+ ICameraDeviceSession::configureStreams_cb);
+
+ Return<void> configureStreams_3_3(
+ const V3_2::StreamConfiguration&,
+ ICameraDeviceSession::configureStreams_3_3_cb);
+
+ Return<void> configureStreams_3_4(
+ const V3_4::StreamConfiguration& requestedConfiguration,
+ ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb);
+
+ Return<void> getCaptureRequestMetadataQueue(
+ ICameraDeviceSession::getCaptureRequestMetadataQueue_cb);
+
+ Return<void> getCaptureResultMetadataQueue(
+ ICameraDeviceSession::getCaptureResultMetadataQueue_cb);
+
+ Return<void> processCaptureRequest(
+ const hidl_vec<CaptureRequest>&, const hidl_vec<BufferCache>&,
+ ICameraDeviceSession::processCaptureRequest_cb);
+
+ Return<Status> flush();
+ Return<void> close();
+
+ Return<void> processCaptureRequest_3_4(
+ const hidl_vec<V3_4::CaptureRequest>& requests,
+ const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+ ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb);
+
+ private:
+ struct ReadVsockRequest {
+ std::vector<uint64_t> buffer_ids;
+ uint32_t frame_number;
+ nsecs_t timestamp;
+ common::V1_0::helper::CameraMetadata settings;
+ uint32_t buffer_count;
+ };
+ struct VsockRequestComparator {
+ bool operator()(const ReadVsockRequest& lhs, const ReadVsockRequest& rhs) {
+ return lhs.frame_number > rhs.frame_number;
+ }
+ };
+ void updateBufferCaches(const hidl_vec<BufferCache>& to_remove);
+ Status configureStreams(const V3_2::StreamConfiguration& config,
+ V3_3::HalStreamConfiguration* out);
+ unsigned int getBlobSize(
+ const V3_4::StreamConfiguration& requestedConfiguration);
+ Status isStreamConfigurationSupported(
+ const V3_2::StreamConfiguration& config);
+ void updateStreamInfo(const V3_2::StreamConfiguration& config);
+ Status processOneCaptureRequest(const CaptureRequest& request);
+ bool getRequestSettingsFmq(uint64_t size,
+ V3_2::CameraMetadata& hidl_settings);
+ void processRequestLoop(unsigned int timeout);
+ bool getRequestFromQueue(ReadVsockRequest& request, unsigned int timeout_ms);
+ void putRequestToQueue(const ReadVsockRequest& request);
+ void fillCaptureResult(common::V1_0::helper::CameraMetadata& md,
+ nsecs_t timestamp);
+ void notifyShutter(uint32_t frame_number, nsecs_t timestamp);
+ void notifyError(uint32_t frame_number, int32_t stream_id, ErrorCode code);
+ void tryWriteFmqResult(V3_2::CaptureResult& result);
+ VsockCameraMetadata camera_characteristics_;
+ std::shared_ptr<cuttlefish::VsockFrameProvider> frame_provider_;
+ const sp<ICameraDeviceCallback> callback_;
+ std::unique_ptr<MessageQueue<uint8_t, kSynchronizedReadWrite>> request_queue_;
+ std::shared_ptr<MessageQueue<uint8_t, kSynchronizedReadWrite>> result_queue_;
+ std::mutex settings_mutex_;
+ common::V1_0::helper::CameraMetadata latest_request_settings_;
+
+ StreamBufferCache buffer_cache_;
+ std::map<int32_t, Stream> stream_cache_;
+
+ std::mutex request_mutex_;
+ std::condition_variable request_available_;
+ std::condition_variable queue_empty_;
+ std::priority_queue<ReadVsockRequest, std::vector<ReadVsockRequest>,
+ VsockRequestComparator>
+ pending_requests_;
+ std::thread request_processor_;
+ std::atomic<bool> process_requests_;
+ std::atomic<bool> flushing_requests_;
+
+ unsigned int max_blob_size_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_metadata.cpp b/guest/hals/camera/vsock_camera_metadata.cpp
new file mode 100644
index 0000000..2e92970
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_metadata.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "vsock_camera_metadata.h"
+
+#include <hardware/camera3.h>
+#include <utils/misc.h>
+#include <vector>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+namespace {
+// Mostly copied from ExternalCameraDevice
+const uint8_t kHardwarelevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
+const uint8_t kAberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
+const uint8_t kAvailableAberrationModes[] = {
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF};
+const int32_t kExposureCompensation = 0;
+const uint8_t kAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+const int32_t kControlMaxRegions[] = {/*AE*/ 0, /*AWB*/ 0, /*AF*/ 0};
+const uint8_t kVideoStabilizationMode =
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+const uint8_t kAwbAvailableMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+const uint8_t kAePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
+const uint8_t kAeAvailableMode = ANDROID_CONTROL_AE_MODE_ON;
+const uint8_t kAvailableFffect = ANDROID_CONTROL_EFFECT_MODE_OFF;
+const uint8_t kControlMode = ANDROID_CONTROL_MODE_AUTO;
+const uint8_t kControlAvailableModes[] = {ANDROID_CONTROL_MODE_OFF,
+ ANDROID_CONTROL_MODE_AUTO};
+const uint8_t kEdgeMode = ANDROID_EDGE_MODE_OFF;
+const uint8_t kFlashInfo = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
+const uint8_t kFlashMode = ANDROID_FLASH_MODE_OFF;
+const uint8_t kHotPixelMode = ANDROID_HOT_PIXEL_MODE_OFF;
+const uint8_t kJpegQuality = 90;
+const int32_t kJpegOrientation = 0;
+const int32_t kThumbnailSize[] = {240, 180};
+const int32_t kJpegAvailableThumbnailSizes[] = {0, 0, 240, 180};
+const uint8_t kFocusDistanceCalibration =
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED;
+const uint8_t kOpticalStabilizationMode =
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+const uint8_t kFacing = ANDROID_LENS_FACING_EXTERNAL;
+const float kLensMinFocusDistance = 0.0f;
+const uint8_t kNoiseReductionMode = ANDROID_NOISE_REDUCTION_MODE_OFF;
+const int32_t kPartialResultCount = 1;
+const uint8_t kRequestPipelineMaxDepth = 4;
+const int32_t kRequestMaxNumInputStreams = 0;
+const float kScalerAvailableMaxDigitalZoom[] = {1};
+const uint8_t kCroppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
+const int32_t kTestPatternMode = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
+const int32_t kTestPatternModes[] = {ANDROID_SENSOR_TEST_PATTERN_MODE_OFF};
+const uint8_t kTimestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
+const int32_t kOrientation = 0;
+const uint8_t kAvailableShadingMode = ANDROID_SHADING_MODE_OFF;
+const uint8_t kFaceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+const int32_t kMaxFaceCount = 0;
+const uint8_t kAvailableHotpixelMode =
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF;
+const uint8_t kLensShadingMapMode =
+ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+const int32_t kMaxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
+const int32_t kControlAeCompensationRange[] = {0, 0};
+const camera_metadata_rational_t kControlAeCompensationStep[] = {{0, 1}};
+const uint8_t kAfTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
+const uint8_t kAfMode = ANDROID_CONTROL_AF_MODE_OFF;
+const uint8_t kAfAvailableModes[] = {ANDROID_CONTROL_AF_MODE_OFF};
+const uint8_t kAvailableSceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED;
+const uint8_t kAeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
+const uint8_t kAwbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
+const int32_t kHalFormats[] = {HAL_PIXEL_FORMAT_BLOB,
+ HAL_PIXEL_FORMAT_YCbCr_420_888,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED};
+const int32_t kRequestMaxNumOutputStreams[] = {
+ /*RAW*/ 0,
+ /*Processed*/ 2,
+ /*Stall*/ 1};
+const uint8_t kAvailableCapabilities[] = {
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE};
+const int32_t kAvailableRequestKeys[] = {
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+ ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+ ANDROID_CONTROL_AE_LOCK,
+ ANDROID_CONTROL_AE_MODE,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ ANDROID_CONTROL_AF_MODE,
+ ANDROID_CONTROL_AF_TRIGGER,
+ ANDROID_CONTROL_AWB_LOCK,
+ ANDROID_CONTROL_AWB_MODE,
+ ANDROID_CONTROL_CAPTURE_INTENT,
+ ANDROID_CONTROL_EFFECT_MODE,
+ ANDROID_CONTROL_MODE,
+ ANDROID_CONTROL_SCENE_MODE,
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+ ANDROID_FLASH_MODE,
+ ANDROID_JPEG_ORIENTATION,
+ ANDROID_JPEG_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_SIZE,
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+ ANDROID_NOISE_REDUCTION_MODE,
+ ANDROID_SCALER_CROP_REGION,
+ ANDROID_SENSOR_TEST_PATTERN_MODE,
+ ANDROID_STATISTICS_FACE_DETECT_MODE,
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE};
+const int32_t kAvailableResultKeys[] = {
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+ ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+ ANDROID_CONTROL_AE_LOCK,
+ ANDROID_CONTROL_AE_MODE,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+ ANDROID_CONTROL_AE_STATE,
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ ANDROID_CONTROL_AF_MODE,
+ ANDROID_CONTROL_AF_STATE,
+ ANDROID_CONTROL_AF_TRIGGER,
+ ANDROID_CONTROL_AWB_LOCK,
+ ANDROID_CONTROL_AWB_MODE,
+ ANDROID_CONTROL_AWB_STATE,
+ ANDROID_CONTROL_CAPTURE_INTENT,
+ ANDROID_CONTROL_EFFECT_MODE,
+ ANDROID_CONTROL_MODE,
+ ANDROID_CONTROL_SCENE_MODE,
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+ ANDROID_FLASH_MODE,
+ ANDROID_FLASH_STATE,
+ ANDROID_JPEG_ORIENTATION,
+ ANDROID_JPEG_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_SIZE,
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+ ANDROID_NOISE_REDUCTION_MODE,
+ ANDROID_REQUEST_PIPELINE_DEPTH,
+ ANDROID_SCALER_CROP_REGION,
+ ANDROID_SENSOR_TIMESTAMP,
+ ANDROID_STATISTICS_FACE_DETECT_MODE,
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
+ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
+ ANDROID_STATISTICS_SCENE_FLICKER};
+const int32_t kAvailableCharacteristicsKeys[] = {
+ ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+ ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+ ANDROID_CONTROL_AE_COMPENSATION_STEP,
+ ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+ ANDROID_CONTROL_AF_AVAILABLE_MODES,
+ ANDROID_CONTROL_AVAILABLE_EFFECTS,
+ ANDROID_CONTROL_AVAILABLE_MODES,
+ ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+ ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+ ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+ ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+ ANDROID_CONTROL_MAX_REGIONS,
+ ANDROID_FLASH_INFO_AVAILABLE,
+ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+ ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+ ANDROID_LENS_FACING,
+ ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+ ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+ ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+ ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
+ ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+ ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+ ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
+ ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ ANDROID_SCALER_CROPPING_TYPE,
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+ ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
+ ANDROID_SENSOR_ORIENTATION,
+ ANDROID_SHADING_AVAILABLE_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+ ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+ ANDROID_SYNC_MAX_LATENCY};
+const std::map<RequestTemplate, uint8_t> kTemplateToIntent = {
+ {RequestTemplate::PREVIEW, ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW},
+ {RequestTemplate::STILL_CAPTURE,
+ ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE},
+ {RequestTemplate::VIDEO_RECORD,
+ ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD},
+ {RequestTemplate::VIDEO_SNAPSHOT,
+ ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT},
+};
+} // namespace
+
+// Constructor sets the default characteristics for vsock camera
+VsockCameraMetadata::VsockCameraMetadata(int32_t width, int32_t height,
+ int32_t fps)
+ : width_(width), height_(height), fps_(fps) {
+ update(ANDROID_CONTROL_AE_COMPENSATION_RANGE, kControlAeCompensationRange,
+ NELEM(kControlAeCompensationRange));
+ update(ANDROID_CONTROL_AE_COMPENSATION_STEP, kControlAeCompensationStep,
+ NELEM(kControlAeCompensationStep));
+ update(ANDROID_CONTROL_AF_AVAILABLE_MODES, kAfAvailableModes,
+ NELEM(kAfAvailableModes));
+ update(ANDROID_CONTROL_AVAILABLE_SCENE_MODES, &kAvailableSceneMode, 1);
+ update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &kAeLockAvailable, 1);
+ update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &kAwbLockAvailable, 1);
+ update(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+ kScalerAvailableMaxDigitalZoom, NELEM(kScalerAvailableMaxDigitalZoom));
+ update(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, kAvailableCapabilities,
+ NELEM(kAvailableCapabilities));
+ update(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &kHardwarelevel, 1);
+ update(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+ kAvailableAberrationModes, NELEM(kAvailableAberrationModes));
+ update(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, &kAntibandingMode, 1);
+ update(ANDROID_CONTROL_MAX_REGIONS, kControlMaxRegions,
+ NELEM(kControlMaxRegions));
+ update(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+ &kVideoStabilizationMode, 1);
+ update(ANDROID_CONTROL_AWB_AVAILABLE_MODES, &kAwbAvailableMode, 1);
+ update(ANDROID_CONTROL_AE_AVAILABLE_MODES, &kAeAvailableMode, 1);
+ update(ANDROID_CONTROL_AVAILABLE_EFFECTS, &kAvailableFffect, 1);
+ update(ANDROID_CONTROL_AVAILABLE_MODES, kControlAvailableModes,
+ NELEM(kControlAvailableModes));
+ update(ANDROID_EDGE_AVAILABLE_EDGE_MODES, &kEdgeMode, 1);
+ update(ANDROID_FLASH_INFO_AVAILABLE, &kFlashInfo, 1);
+ update(ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES, &kHotPixelMode, 1);
+ update(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, kJpegAvailableThumbnailSizes,
+ NELEM(kJpegAvailableThumbnailSizes));
+ update(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+ &kFocusDistanceCalibration, 1);
+ update(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, &kLensMinFocusDistance, 1);
+ update(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+ &kOpticalStabilizationMode, 1);
+ update(ANDROID_LENS_FACING, &kFacing, 1);
+ update(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+ &kNoiseReductionMode, 1);
+ update(ANDROID_NOISE_REDUCTION_MODE, &kNoiseReductionMode, 1);
+ update(ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &kPartialResultCount, 1);
+ update(ANDROID_REQUEST_PIPELINE_MAX_DEPTH, &kRequestPipelineMaxDepth, 1);
+ update(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, kRequestMaxNumOutputStreams,
+ NELEM(kRequestMaxNumOutputStreams));
+ update(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, &kRequestMaxNumInputStreams, 1);
+ update(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+ kScalerAvailableMaxDigitalZoom, NELEM(kScalerAvailableMaxDigitalZoom));
+ update(ANDROID_SCALER_CROPPING_TYPE, &kCroppingType, 1);
+ update(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES, kTestPatternModes,
+ NELEM(kTestPatternModes));
+ update(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, &kTimestampSource, 1);
+ update(ANDROID_SENSOR_ORIENTATION, &kOrientation, 1);
+ update(ANDROID_SHADING_AVAILABLE_MODES, &kAvailableShadingMode, 1);
+ update(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, &kFaceDetectMode,
+ 1);
+ update(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, &kMaxFaceCount, 1);
+ update(ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+ &kAvailableHotpixelMode, 1);
+ update(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+ &kLensShadingMapMode, 1);
+ update(ANDROID_SYNC_MAX_LATENCY, &kMaxLatency, 1);
+ update(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, kAvailableRequestKeys,
+ NELEM(kAvailableRequestKeys));
+ update(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, kAvailableResultKeys,
+ NELEM(kAvailableResultKeys));
+ update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+ kAvailableCharacteristicsKeys, NELEM(kAvailableCharacteristicsKeys));
+
+ // assume max 2bytes/pixel + info because client might provide us pngs
+ const int32_t jpeg_max_size = width * height * 2 + sizeof(camera3_jpeg_blob);
+ update(ANDROID_JPEG_MAX_SIZE, &jpeg_max_size, 1);
+
+ std::vector<int64_t> min_frame_durations;
+ std::vector<int32_t> stream_configurations;
+ std::vector<int64_t> stall_durations;
+
+ int64_t frame_duration = 1000000000L / fps;
+ for (const auto& format : kHalFormats) {
+ stream_configurations.push_back(format);
+ min_frame_durations.push_back(format);
+ stall_durations.push_back(format);
+ stream_configurations.push_back(width);
+ min_frame_durations.push_back(width);
+ stall_durations.push_back(width);
+ stream_configurations.push_back(height);
+ min_frame_durations.push_back(height);
+ stall_durations.push_back(height);
+ stream_configurations.push_back(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
+ min_frame_durations.push_back(frame_duration);
+ stall_durations.push_back((format == HAL_PIXEL_FORMAT_BLOB) ? 2000000000L
+ : 0);
+ }
+ update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ stream_configurations.data(), stream_configurations.size());
+ update(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+ min_frame_durations.data(), min_frame_durations.size());
+ update(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, stall_durations.data(),
+ stall_durations.size());
+
+ int32_t active_array_size[] = {0, 0, width, height};
+ update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ active_array_size, NELEM(active_array_size));
+ update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, active_array_size,
+ NELEM(active_array_size));
+
+ int32_t pixel_array_size[] = {width, height};
+ update(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, pixel_array_size,
+ NELEM(pixel_array_size));
+
+ int32_t max_frame_rate = fps;
+ int32_t min_frame_rate = max_frame_rate / 2;
+ int32_t frame_rates[] = {min_frame_rate, max_frame_rate};
+ update(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, frame_rates,
+ NELEM(frame_rates));
+ int64_t max_frame_duration = 1000000000L / min_frame_rate;
+ update(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, &max_frame_duration, 1);
+}
+
+VsockCameraRequestMetadata::VsockCameraRequestMetadata(int32_t fps,
+ RequestTemplate type) {
+ update(ANDROID_COLOR_CORRECTION_ABERRATION_MODE, &kAberrationMode, 1);
+ update(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, &kExposureCompensation, 1);
+ update(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, &kVideoStabilizationMode, 1);
+ update(ANDROID_CONTROL_AWB_MODE, &kAwbAvailableMode, 1);
+ update(ANDROID_CONTROL_AE_MODE, &kAeAvailableMode, 1);
+ update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, &kAePrecaptureTrigger, 1);
+ update(ANDROID_CONTROL_AF_MODE, &kAfMode, 1);
+ update(ANDROID_CONTROL_AF_TRIGGER, &kAfTrigger, 1);
+ update(ANDROID_CONTROL_SCENE_MODE, &kAvailableSceneMode, 1);
+ update(ANDROID_CONTROL_EFFECT_MODE, &kAvailableFffect, 1);
+ update(ANDROID_FLASH_MODE, &kFlashMode, 1);
+ update(ANDROID_JPEG_THUMBNAIL_SIZE, kThumbnailSize, NELEM(kThumbnailSize));
+ update(ANDROID_JPEG_QUALITY, &kJpegQuality, 1);
+ update(ANDROID_JPEG_THUMBNAIL_QUALITY, &kJpegQuality, 1);
+ update(ANDROID_JPEG_ORIENTATION, &kJpegOrientation, 1);
+ update(ANDROID_LENS_OPTICAL_STABILIZATION_MODE, &kOpticalStabilizationMode,
+ 1);
+ update(ANDROID_NOISE_REDUCTION_MODE, &kNoiseReductionMode, 1);
+ update(ANDROID_SENSOR_TEST_PATTERN_MODE, &kTestPatternMode, 1);
+ update(ANDROID_STATISTICS_FACE_DETECT_MODE, &kFaceDetectMode, 1);
+ update(ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE, &kAvailableHotpixelMode, 1);
+
+ int32_t max_frame_rate = fps;
+ int32_t min_frame_rate = max_frame_rate / 2;
+ int32_t frame_rates[] = {min_frame_rate, max_frame_rate};
+ update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE, frame_rates, NELEM(frame_rates));
+
+ update(ANDROID_CONTROL_AE_ANTIBANDING_MODE, &kAntibandingMode, 1);
+ update(ANDROID_CONTROL_MODE, &kControlMode, 1);
+
+ auto it = kTemplateToIntent.find(type);
+ if (it != kTemplateToIntent.end()) {
+ auto intent = it->second;
+ update(ANDROID_CONTROL_CAPTURE_INTENT, &intent, 1);
+ is_valid_ = true;
+ } else {
+ is_valid_ = false;
+ }
+}
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_metadata.h b/guest/hals/camera/vsock_camera_metadata.h
new file mode 100644
index 0000000..d5b43c7
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_metadata.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <CameraMetadata.h>
+#include <android/hardware/camera/device/3.2/ICameraDevice.h>
+
+namespace android::hardware::camera::device::V3_4::implementation {
+
+// Small wrappers for mostly hard-coded camera metadata
+// Some parameters are calculated from remote camera frame size and fps
+using ::android::hardware::camera::common::V1_0::helper::CameraMetadata;
+class VsockCameraMetadata : public CameraMetadata {
+ public:
+ VsockCameraMetadata(int32_t width, int32_t height, int32_t fps);
+
+ int32_t getPreferredWidth() const { return width_; }
+ int32_t getPreferredHeight() const { return height_; }
+ int32_t getPreferredFps() const { return fps_; }
+
+ private:
+ int32_t width_;
+ int32_t height_;
+ int32_t fps_;
+};
+
+using ::android::hardware::camera::device::V3_2::RequestTemplate;
+class VsockCameraRequestMetadata : public CameraMetadata {
+ public:
+ VsockCameraRequestMetadata(int32_t fps, RequestTemplate type);
+ // Tells whether the metadata has been successfully constructed
+ // from the parameters
+ bool isValid() const { return is_valid_; }
+
+ private:
+ bool is_valid_;
+};
+
+} // namespace android::hardware::camera::device::V3_4::implementation
diff --git a/guest/hals/camera/vsock_camera_provider_2_7.cpp b/guest/hals/camera/vsock_camera_provider_2_7.cpp
new file mode 100644
index 0000000..dbe0092
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_provider_2_7.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "VsockCameraProvider"
+#include "vsock_camera_provider_2_7.h"
+#include <cutils/properties.h>
+#include <log/log.h>
+#include "vsock_camera_server.h"
+
+namespace android::hardware::camera::provider::V2_7::implementation {
+
+namespace {
+VsockCameraServer gCameraServer;
+constexpr auto kDeviceName = "[email protected]/external/0";
+} // namespace
+
+using android::hardware::camera::provider::V2_7::ICameraProvider;
+extern "C" ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name) {
+ return (strcmp(name, "external/0") == 0)
+ ? new VsockCameraProvider(&gCameraServer)
+ : nullptr;
+}
+
+VsockCameraProvider::VsockCameraProvider(VsockCameraServer* server) {
+ server_ = server;
+ if (!server->isRunning()) {
+ constexpr static const auto camera_port_property =
+ "ro.boot.vsock_camera_port";
+ constexpr static const auto camera_cid_property =
+ "ro.boot.vsock_camera_cid";
+ auto port = property_get_int32(camera_port_property, -1);
+ auto cid = property_get_int32(camera_cid_property, -1);
+ if (port > 0) {
+ server->start(port, cid);
+ }
+ }
+}
+
+VsockCameraProvider::~VsockCameraProvider() {
+ server_->setConnectedCallback(nullptr);
+}
+
+Return<Status> VsockCameraProvider::setCallback(
+ const sp<ICameraProviderCallback>& callback) {
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ callbacks_ = callback;
+ }
+ server_->setConnectedCallback(
+ [this](std::shared_ptr<cuttlefish::VsockConnection> connection,
+ VsockCameraDevice::Settings settings) {
+ connection_ = connection;
+ settings_ = settings;
+ deviceAdded(kDeviceName);
+ connection_->SetDisconnectCallback(
+ [this] { deviceRemoved(kDeviceName); });
+ });
+ return Status::OK;
+}
+
+Return<void> VsockCameraProvider::getVendorTags(
+ ICameraProvider::getVendorTags_cb _hidl_cb) {
+ // No vendor tag support
+ hidl_vec<VendorTagSection> empty;
+ _hidl_cb(Status::OK, empty);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::getCameraIdList(
+ ICameraProvider::getCameraIdList_cb _hidl_cb) {
+ // External camera HAL always report 0 camera, and extra cameras
+ // are just reported via cameraDeviceStatusChange callbacks
+ hidl_vec<hidl_string> empty;
+ _hidl_cb(Status::OK, empty);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::isSetTorchModeSupported(
+ ICameraProvider::isSetTorchModeSupported_cb _hidl_cb) {
+ // setTorchMode API is supported, though right now no external camera device
+ // has a flash unit.
+ _hidl_cb(Status::OK, true);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::getCameraDeviceInterface_V1_x(
+ const hidl_string&,
+ ICameraProvider::getCameraDeviceInterface_V1_x_cb _hidl_cb) {
+ // External Camera HAL does not support HAL1
+ _hidl_cb(Status::OPERATION_NOT_SUPPORTED, nullptr);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::getCameraDeviceInterface_V3_x(
+ const hidl_string& name_hidl_str,
+ ICameraProvider::getCameraDeviceInterface_V3_x_cb _hidl_cb) {
+ std::string name(name_hidl_str.c_str());
+ if (name != kDeviceName) {
+ _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+ return Void();
+ }
+
+ _hidl_cb(Status::OK, new VsockCameraDevice(name, settings_, connection_));
+ return Void();
+}
+
+Return<void> VsockCameraProvider::notifyDeviceStateChange(
+ hardware::hidl_bitfield<DeviceState> /*newState*/) {
+ return Void();
+}
+
+Return<void> VsockCameraProvider::getConcurrentStreamingCameraIds(
+ getConcurrentStreamingCameraIds_cb _hidl_cb) {
+ hidl_vec<hidl_vec<hidl_string>> hidl_camera_id_combinations;
+ _hidl_cb(Status::OK, hidl_camera_id_combinations);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::isConcurrentStreamCombinationSupported(
+ const hidl_vec<::android::hardware::camera::provider::V2_6::CameraIdAndStreamCombination>&,
+ isConcurrentStreamCombinationSupported_cb _hidl_cb) {
+ _hidl_cb(Status::OK, false);
+ return Void();
+}
+
+Return<void> VsockCameraProvider::isConcurrentStreamCombinationSupported_2_7(
+ const hidl_vec<::android::hardware::camera::provider::V2_7::CameraIdAndStreamCombination>&,
+ isConcurrentStreamCombinationSupported_2_7_cb _hidl_cb) {
+ _hidl_cb(Status::OK, false);
+ return Void();
+}
+
+void VsockCameraProvider::deviceAdded(const char* name) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (callbacks_ != nullptr) {
+ callbacks_->cameraDeviceStatusChange(name, CameraDeviceStatus::PRESENT);
+ }
+}
+
+void VsockCameraProvider::deviceRemoved(const char* name) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (callbacks_ != nullptr) {
+ callbacks_->cameraDeviceStatusChange(name, CameraDeviceStatus::NOT_PRESENT);
+ }
+}
+
+} // namespace android::hardware::camera::provider::V2_7::implementation
diff --git a/guest/hals/camera/vsock_camera_provider_2_7.h b/guest/hals/camera/vsock_camera_provider_2_7.h
new file mode 100644
index 0000000..7ab9997
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_provider_2_7.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <mutex>
+
+#include <android/hardware/camera/provider/2.7/ICameraProvider.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <json/json.h>
+
+#include "vsock_camera_device_3_4.h"
+#include "vsock_camera_server.h"
+#include "vsock_connection.h"
+
+namespace android::hardware::camera::provider::V2_7::implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hardware::camera::common::V1_0::CameraDeviceStatus;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::VendorTagSection;
+using ::android::hardware::camera::device::V3_4::implementation::
+ VsockCameraDevice;
+using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
+using ::android::hardware::camera::provider::V2_5::DeviceState;
+using ::android::hardware::camera::provider::V2_7::ICameraProvider;
+
+class VsockCameraProvider : public ICameraProvider {
+ public:
+ VsockCameraProvider(VsockCameraServer* server);
+ ~VsockCameraProvider();
+
+ Return<Status> setCallback(
+ const sp<ICameraProviderCallback>& callback) override;
+ Return<void> getVendorTags(getVendorTags_cb _hidl_cb) override;
+ Return<void> getCameraIdList(getCameraIdList_cb _hidl_cb) override;
+ Return<void> isSetTorchModeSupported(
+ isSetTorchModeSupported_cb _hidl_cb) override;
+ Return<void> getCameraDeviceInterface_V1_x(
+ const hidl_string& cameraDeviceName,
+ getCameraDeviceInterface_V1_x_cb _hidl_cb) override;
+ Return<void> getCameraDeviceInterface_V3_x(
+ const hidl_string& cameraDeviceName,
+ getCameraDeviceInterface_V3_x_cb _hidl_cb) override;
+ Return<void> notifyDeviceStateChange(
+ hardware::hidl_bitfield<DeviceState> newState) override;
+ Return<void> getConcurrentStreamingCameraIds(
+ getConcurrentStreamingCameraIds_cb _hidl_cb) override;
+ Return<void> isConcurrentStreamCombinationSupported(
+ const hidl_vec<::android::hardware::camera::provider::V2_6::CameraIdAndStreamCombination>& configs,
+ isConcurrentStreamCombinationSupported_cb _hidl_cb) override;
+ Return<void> isConcurrentStreamCombinationSupported_2_7(
+ const hidl_vec<::android::hardware::camera::provider::V2_7::CameraIdAndStreamCombination>& configs,
+ isConcurrentStreamCombinationSupported_2_7_cb _hidl_cb) override;
+
+ private:
+ void deviceRemoved(const char* name);
+ void deviceAdded(const char* name);
+ std::mutex mutex_;
+ sp<ICameraProviderCallback> callbacks_;
+ std::shared_ptr<cuttlefish::VsockConnection> connection_;
+ VsockCameraDevice::Settings settings_;
+ VsockCameraServer* server_;
+};
+
+} // namespace android::hardware::camera::provider::V2_7::implementation
diff --git a/guest/hals/camera/vsock_camera_server.cpp b/guest/hals/camera/vsock_camera_server.cpp
new file mode 100644
index 0000000..ddb574c
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_server.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "VsockCameraServer"
+#include "vsock_camera_server.h"
+#include <log/log.h>
+
+namespace android::hardware::camera::provider::V2_7::implementation {
+
+using ::android::hardware::camera::device::V3_4::implementation::
+ VsockCameraDevice;
+namespace {
+
+bool containsValidSettings(const VsockCameraDevice::Settings& settings) {
+ return settings.width > 0 && settings.height > 0 && settings.frame_rate > 0.0;
+}
+
+bool readSettingsFromJson(VsockCameraDevice::Settings& settings,
+ const Json::Value& json) {
+ VsockCameraDevice::Settings new_settings;
+ new_settings.width = json["width"].asInt();
+ new_settings.height = json["height"].asInt();
+ new_settings.frame_rate = json["frame_rate"].asDouble();
+ if (containsValidSettings(new_settings)) {
+ settings = new_settings;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace
+
+VsockCameraServer::VsockCameraServer() {
+ ALOGI("%s: Create server", __FUNCTION__);
+ connection_ = std::make_shared<cuttlefish::VsockServerConnection>();
+}
+
+VsockCameraServer::~VsockCameraServer() {
+ ALOGI("%s: Destroy server", __FUNCTION__);
+ stop();
+}
+
+void VsockCameraServer::start(unsigned int port, unsigned int cid) {
+ stop();
+ is_running_ = true;
+ server_thread_ = std::thread([this, port, cid] { serverLoop(port, cid); });
+}
+
+void VsockCameraServer::stop() {
+ connection_->ServerShutdown();
+ is_running_ = false;
+ if (server_thread_.joinable()) {
+ server_thread_.join();
+ }
+}
+
+void VsockCameraServer::setConnectedCallback(callback_t callback) {
+ connected_callback_ = callback;
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ if (callback && connection_->IsConnected() &&
+ containsValidSettings(settings_)) {
+ callback(connection_, settings_);
+ }
+}
+
+void VsockCameraServer::serverLoop(unsigned int port, unsigned int cid) {
+ while (is_running_.load()) {
+ ALOGI("%s: Accepting connections...", __FUNCTION__);
+ if (connection_->Connect(port, cid)) {
+ auto json_settings = connection_->ReadJsonMessage();
+ VsockCameraDevice::Settings settings;
+ if (readSettingsFromJson(settings, json_settings)) {
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ settings_ = settings;
+ if (connected_callback_) {
+ connected_callback_(connection_, settings);
+ }
+ ALOGI("%s: Client connected", __FUNCTION__);
+ } else {
+ ALOGE("%s: Could not read settings", __FUNCTION__);
+ }
+ } else {
+ ALOGE("%s: Accepting connections failed", __FUNCTION__);
+ }
+ }
+ ALOGI("%s: Exiting", __FUNCTION__);
+}
+
+} // namespace android::hardware::camera::provider::V2_7::implementation
diff --git a/guest/hals/camera/vsock_camera_server.h b/guest/hals/camera/vsock_camera_server.h
new file mode 100644
index 0000000..f807dcf
--- /dev/null
+++ b/guest/hals/camera/vsock_camera_server.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <atomic>
+#include <thread>
+#include "vsock_camera_device_3_4.h"
+#include "vsock_connection.h"
+
+namespace android::hardware::camera::provider::V2_7::implementation {
+
+using ::android::hardware::camera::device::V3_4::implementation::
+ VsockCameraDevice;
+
+class VsockCameraServer {
+ public:
+ VsockCameraServer();
+ ~VsockCameraServer();
+
+ VsockCameraServer(const VsockCameraServer&) = delete;
+ VsockCameraServer& operator=(const VsockCameraServer&) = delete;
+
+ bool isRunning() const { return is_running_.load(); }
+
+ void start(unsigned int port, unsigned int cid);
+ void stop();
+
+ using callback_t =
+ std::function<void(std::shared_ptr<cuttlefish::VsockConnection>,
+ VsockCameraDevice::Settings)>;
+ void setConnectedCallback(callback_t callback);
+
+ private:
+ void serverLoop(unsigned int port, unsigned int cid);
+ std::thread server_thread_;
+ std::atomic<bool> is_running_;
+ std::shared_ptr<cuttlefish::VsockServerConnection> connection_;
+ std::mutex settings_mutex_;
+ VsockCameraDevice::Settings settings_;
+ callback_t connected_callback_;
+};
+
+} // namespace android::hardware::camera::provider::V2_7::implementation
diff --git a/guest/hals/camera/vsock_frame_provider.cpp b/guest/hals/camera/vsock_frame_provider.cpp
new file mode 100644
index 0000000..435d5f9
--- /dev/null
+++ b/guest/hals/camera/vsock_frame_provider.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "vsock_frame_provider.h"
+#include <hardware/camera3.h>
+#include <libyuv.h>
+#include <cstring>
+#define LOG_TAG "VsockFrameProvider"
+#include <log/log.h>
+
+namespace cuttlefish {
+
+VsockFrameProvider::~VsockFrameProvider() { stop(); }
+
+void VsockFrameProvider::start(
+ std::shared_ptr<cuttlefish::VsockConnection> connection, uint32_t width,
+ uint32_t height) {
+ stop();
+ running_ = true;
+ connection_ = connection;
+ reader_thread_ =
+ std::thread([this, width, height] { VsockReadLoop(width, height); });
+}
+
+void VsockFrameProvider::stop() {
+ running_ = false;
+ jpeg_pending_ = false;
+ if (reader_thread_.joinable()) {
+ reader_thread_.join();
+ }
+ connection_ = nullptr;
+}
+
+bool VsockFrameProvider::waitYUVFrame(unsigned int max_wait_ms) {
+ auto timeout = std::chrono::milliseconds(max_wait_ms);
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ std::unique_lock<std::mutex> lock(frame_mutex_);
+ return yuv_frame_updated_.wait_for(
+ lock, timeout, [this, now] { return timestamp_.load() > now; });
+}
+
+void VsockFrameProvider::requestJpeg() {
+ jpeg_pending_ = true;
+ Json::Value message;
+ message["event"] = "VIRTUAL_DEVICE_CAPTURE_IMAGE";
+ if (connection_) {
+ connection_->WriteMessage(message);
+ }
+}
+
+void VsockFrameProvider::cancelJpegRequest() { jpeg_pending_ = false; }
+
+bool VsockFrameProvider::copyYUVFrame(uint32_t w, uint32_t h, YCbCrLayout dst) {
+ size_t y_size = w * h;
+ size_t cbcr_size = (w / 2) * (h / 2);
+ size_t total_size = y_size + 2 * cbcr_size;
+ std::lock_guard<std::mutex> lock(frame_mutex_);
+ if (frame_.size() < total_size) {
+ ALOGE("%s: %zu is too little for %ux%u frame", __FUNCTION__, frame_.size(),
+ w, h);
+ return false;
+ }
+ if (dst.y == nullptr) {
+ ALOGE("%s: Destination is nullptr!", __FUNCTION__);
+ return false;
+ }
+ YCbCrLayout src{.y = static_cast<void*>(frame_.data()),
+ .cb = static_cast<void*>(frame_.data() + y_size),
+ .cr = static_cast<void*>(frame_.data() + y_size + cbcr_size),
+ .yStride = w,
+ .cStride = w / 2,
+ .chromaStep = 1};
+ uint8_t* src_y = static_cast<uint8_t*>(src.y);
+ uint8_t* dst_y = static_cast<uint8_t*>(dst.y);
+ uint8_t* src_cb = static_cast<uint8_t*>(src.cb);
+ uint8_t* dst_cb = static_cast<uint8_t*>(dst.cb);
+ uint8_t* src_cr = static_cast<uint8_t*>(src.cr);
+ uint8_t* dst_cr = static_cast<uint8_t*>(dst.cr);
+ libyuv::CopyPlane(src_y, src.yStride, dst_y, dst.yStride, w, h);
+ if (dst.chromaStep == 1) {
+ // Planar
+ libyuv::CopyPlane(src_cb, src.cStride, dst_cb, dst.cStride, w / 2, h / 2);
+ libyuv::CopyPlane(src_cr, src.cStride, dst_cr, dst.cStride, w / 2, h / 2);
+ } else if (dst.chromaStep == 2 && dst_cr - dst_cb == 1) {
+ // Interleaved cb/cr planes starting with cb
+ libyuv::MergeUVPlane(src_cb, src.cStride, src_cr, src.cStride, dst_cb,
+ dst.cStride, w / 2, h / 2);
+ } else if (dst.chromaStep == 2 && dst_cb - dst_cr == 1) {
+ // Interleaved cb/cr planes starting with cr
+ libyuv::MergeUVPlane(src_cr, src.cStride, src_cb, src.cStride, dst_cr,
+ dst.cStride, w / 2, h / 2);
+ } else {
+ ALOGE("%s: Unsupported interleaved U/V layout", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+bool VsockFrameProvider::copyJpegData(uint32_t size, void* dst) {
+ std::lock_guard<std::mutex> lock(jpeg_mutex_);
+ auto jpeg_header_offset = size - sizeof(struct camera3_jpeg_blob);
+ if (cached_jpeg_.empty()) {
+ ALOGE("%s: No source data", __FUNCTION__);
+ return false;
+ } else if (dst == nullptr) {
+ ALOGE("%s: Destination is nullptr", __FUNCTION__);
+ return false;
+ } else if (jpeg_header_offset <= cached_jpeg_.size()) {
+ ALOGE("%s: %ubyte target buffer too small", __FUNCTION__, size);
+ return false;
+ }
+ std::memcpy(dst, cached_jpeg_.data(), cached_jpeg_.size());
+ struct camera3_jpeg_blob* blob = reinterpret_cast<struct camera3_jpeg_blob*>(
+ static_cast<char*>(dst) + jpeg_header_offset);
+ blob->jpeg_blob_id = CAMERA3_JPEG_BLOB_ID;
+ blob->jpeg_size = cached_jpeg_.size();
+ cached_jpeg_.clear();
+ return true;
+}
+
+bool VsockFrameProvider::isBlob(const std::vector<char>& blob) {
+ bool is_png = blob.size() > 4 && (blob[0] & 0xff) == 0x89 &&
+ (blob[1] & 0xff) == 0x50 && (blob[2] & 0xff) == 0x4e &&
+ (blob[3] & 0xff) == 0x47;
+ bool is_jpeg =
+ blob.size() > 2 && (blob[0] & 0xff) == 0xff && (blob[1] & 0xff) == 0xd8;
+ return is_png || is_jpeg;
+}
+
+bool VsockFrameProvider::framesizeMatches(uint32_t width, uint32_t height,
+ const std::vector<char>& data) {
+ return data.size() == 3 * width * height / 2;
+}
+
+void VsockFrameProvider::VsockReadLoop(uint32_t width, uint32_t height) {
+ jpeg_pending_ = false;
+ while (running_.load() && connection_->ReadMessage(next_frame_)) {
+ if (framesizeMatches(width, height, next_frame_)) {
+ std::lock_guard<std::mutex> lock(frame_mutex_);
+ timestamp_ = systemTime();
+ frame_.swap(next_frame_);
+ yuv_frame_updated_.notify_one();
+ } else if (isBlob(next_frame_)) {
+ std::lock_guard<std::mutex> lock(jpeg_mutex_);
+ bool was_pending = jpeg_pending_.exchange(false);
+ if (was_pending) {
+ cached_jpeg_.swap(next_frame_);
+ }
+ } else {
+ ALOGE("%s: Unexpected data of %zu bytes", __FUNCTION__,
+ next_frame_.size());
+ }
+ }
+ if (!connection_->IsConnected()) {
+ ALOGE("%s: Connection closed - exiting", __FUNCTION__);
+ running_ = false;
+ }
+}
+
+} // namespace cuttlefish
diff --git a/guest/hals/camera/vsock_frame_provider.h b/guest/hals/camera/vsock_frame_provider.h
new file mode 100644
index 0000000..4454d3e
--- /dev/null
+++ b/guest/hals/camera/vsock_frame_provider.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <atomic>
+#include <mutex>
+#include <thread>
+#include <vector>
+#include "utils/Timers.h"
+#include "vsock_connection.h"
+
+namespace cuttlefish {
+
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+
+// VsockFrameProvider reads data from vsock
+// Users can get the data by using copyYUVFrame/copyJpegData methods
+class VsockFrameProvider {
+ public:
+ VsockFrameProvider() = default;
+ ~VsockFrameProvider();
+
+ VsockFrameProvider(const VsockFrameProvider&) = delete;
+ VsockFrameProvider& operator=(const VsockFrameProvider&) = delete;
+
+ void start(std::shared_ptr<cuttlefish::VsockConnection> connection,
+ uint32_t expected_width, uint32_t expected_height);
+ void stop();
+ void requestJpeg();
+ void cancelJpegRequest();
+ bool jpegPending() const { return jpeg_pending_.load(); }
+ bool isRunning() const { return running_.load(); }
+ bool waitYUVFrame(unsigned int max_wait_ms);
+ bool copyYUVFrame(uint32_t width, uint32_t height, YCbCrLayout dst);
+ bool copyJpegData(uint32_t size, void* dst);
+
+ private:
+ bool isBlob(const std::vector<char>& blob);
+ bool framesizeMatches(uint32_t width, uint32_t height,
+ const std::vector<char>& data);
+ void VsockReadLoop(uint32_t expected_width, uint32_t expected_height);
+ std::thread reader_thread_;
+ std::mutex frame_mutex_;
+ std::mutex jpeg_mutex_;
+ std::atomic<nsecs_t> timestamp_;
+ std::atomic<bool> running_;
+ std::atomic<bool> jpeg_pending_;
+ std::vector<char> frame_;
+ std::vector<char> next_frame_;
+ std::vector<char> cached_jpeg_;
+ std::condition_variable yuv_frame_updated_;
+ std::shared_ptr<cuttlefish::VsockConnection> connection_;
+};
+
+} // namespace cuttlefish
diff --git a/guest/hals/ril/reference-libril/Android.bp b/guest/hals/ril/reference-libril/Android.bp
index db265e3..687f95f 100644
--- a/guest/hals/ril/reference-libril/Android.bp
+++ b/guest/hals/ril/reference-libril/Android.bp
@@ -59,3 +59,12 @@
"libprotobuf-c-nano-enable_malloc"
],
}
+
+filegroup {
+ name: "libril-modem-lib-manifests",
+ srcs: [
+ "[email protected]",
+ "[email protected]",
+ ],
+}
+
diff --git a/guest/hals/ril/reference-libril/[email protected] b/guest/hals/ril/reference-libril/[email protected]
new file mode 100644
index 0000000..97bf66d
--- /dev/null
+++ b/guest/hals/ril/reference-libril/[email protected]
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.radio.config</name>
+ <transport>hwbinder</transport>
+ <version>1.3</version>
+ <interface>
+ <name>IRadioConfig</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/guest/hals/ril/reference-libril/[email protected] b/guest/hals/ril/reference-libril/[email protected]
new file mode 100644
index 0000000..8652229
--- /dev/null
+++ b/guest/hals/ril/reference-libril/[email protected]
@@ -0,0 +1,18 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.radio</name>
+ <transport>hwbinder</transport>
+ <version>1.6</version>
+ <interface>
+ <name>IRadio</name>
+ <instance>slot1</instance>
+ <!-- cuttlefish doesn't support SIM slot 2/3 -->
+ </interface>
+ <!-- TODO (b/130079344):
+ <interface>
+ <name>ISap</name>
+ <instance>slot1</instance>
+ </interface>
+ -->
+ </hal>
+</manifest>
diff --git a/guest/hals/ril/reference-ril/reference-ril.c b/guest/hals/ril/reference-ril/reference-ril.c
index de040c8..5d17ab4 100644
--- a/guest/hals/ril/reference-ril/reference-ril.c
+++ b/guest/hals/ril/reference-ril/reference-ril.c
@@ -378,6 +378,9 @@
static bool s_stkServiceRunning = false;
static char *s_stkUnsolResponse = NULL;
+// Next available handle for keep alive session
+static uint32_t s_session_handle = 1;
+
typedef enum {
STK_UNSOL_EVENT_UNKNOWN,
STK_UNSOL_EVENT_NOTIFY,
@@ -4213,6 +4216,13 @@
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
}
+static void requestStartKeepalive(RIL_Token t) {
+ RIL_KeepaliveStatus resp;
+ resp.sessionHandle = s_session_handle++;
+ resp.code = KEEPALIVE_ACTIVE;
+ RIL_onRequestComplete(t, RIL_E_SUCCESS, &resp, sizeof(resp));
+}
+
void getConfigSlotStatus(RIL_SimSlotStatus_V1_2 *pSimSlotStatus) {
if (pSimSlotStatus == NULL) {
return;
@@ -4235,6 +4245,16 @@
pSimSlotStatus->eid = "";
}
+void sendUnsolNetworkScanResult() {
+ RIL_NetworkScanResult scanr;
+ memset(&scanr, 0, sizeof(scanr));
+ scanr.status = COMPLETE;
+ scanr.error = RIL_E_SUCCESS;
+ scanr.network_infos = NULL;
+ scanr.network_infos_length = 0;
+ RIL_onUnsolicitedResponse(RIL_UNSOL_NETWORK_SCAN_RESULT, &scanr, sizeof(scanr));
+}
+
void onIccSlotStatus(RIL_Token t) {
RIL_SimSlotStatus_V1_2 *pSimSlotStatusList =
(RIL_SimSlotStatus_V1_2 *)calloc(SIM_COUNT, sizeof(RIL_SimSlotStatus_V1_2));
@@ -4857,6 +4877,8 @@
// New requests after P.
case RIL_REQUEST_START_NETWORK_SCAN:
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+ // send unsol network scan results after a short while
+ RIL_requestTimedCallback (sendUnsolNetworkScanResult, NULL, &TIMEVAL_SIMPOLL);
break;
case RIL_REQUEST_GET_MODEM_STACK_STATUS:
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
@@ -4976,6 +4998,12 @@
case RIL_REQUEST_SET_DATA_THROTTLING:
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
break;
+ case RIL_REQUEST_START_KEEPALIVE:
+ requestStartKeepalive(t);
+ break;
+ case RIL_REQUEST_STOP_KEEPALIVE:
+ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+ break;
default:
RLOGD("Request not supported. Tech: %d",TECH(sMdmInfo));
RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
diff --git a/guest/hals/rild/Android.bp b/guest/hals/rild/Android.bp
index 9a7ad01..95dd92d 100644
--- a/guest/hals/rild/Android.bp
+++ b/guest/hals/rild/Android.bp
@@ -39,6 +39,7 @@
"libril-modem-lib",
],
init_rc: ["rild_cuttlefish.rc"],
+ vintf_fragments: [":libril-modem-lib-manifests"],
relative_install_path: "hw",
overrides: ["rild"],
}
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index a1ce5eb..b12dfd2 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -1,6 +1,7 @@
#include "host/commands/assemble_cvd/flags.h"
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <gflags/gflags.h>
#include <json/json.h>
@@ -16,6 +17,7 @@
#include <regex>
#include <set>
#include <sstream>
+#include <unordered_map>
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
@@ -54,6 +56,26 @@
"kernel must have been built with CONFIG_RANDOMIZE_BASE "
"disabled.");
+constexpr const char kDisplayHelp[] =
+ "Comma separated key=value pairs of display properties. Supported "
+ "properties:\n"
+ " 'width': required, width of the display in pixels\n"
+ " 'height': required, height of the display in pixels\n"
+ " 'dpi': optional, default 320, density of the display\n"
+ " 'refresh_rate_hz': optional, default 60, display refresh rate in Hertz\n"
+ ". Example usage: \n"
+ "--display0=width=1280,height=720\n"
+ "--display1=width=1440,height=900,dpi=480,refresh_rate_hz=30\n";
+
+// TODO(b/192495477): combine these into a single repeatable '--display' flag
+// when assemble_cvd switches to using the new flag parsing library.
+DEFINE_string(display0, "", kDisplayHelp);
+DEFINE_string(display1, "", kDisplayHelp);
+DEFINE_string(display2, "", kDisplayHelp);
+DEFINE_string(display3, "", kDisplayHelp);
+
+// TODO(b/171305898): mark these as deprecated after multi-display is fully
+// enabled.
DEFINE_int32(x_res, 0, "Width of the screen in pixels");
DEFINE_int32(y_res, 0, "Height of the screen in pixels");
DEFINE_int32(dpi, 0, "Pixels per inch for the screen");
@@ -268,8 +290,6 @@
DEFINE_bool(record_screen, false, "Enable screen recording. "
"Requires --start_webrtc");
-DEFINE_bool(ethernet, false, "Enable Ethernet network interface");
-
DEFINE_bool(smt, false, "Enable simultaneous multithreading (SMT/HT)");
DEFINE_int32(vsock_guest_cid,
@@ -299,6 +319,8 @@
DEFINE_bool(enable_audio, cuttlefish::HostArch() != cuttlefish::Arch::Arm64,
"Whether to play or capture audio");
+DEFINE_uint32(camera_server_port, 0, "camera vsock port");
+
DECLARE_string(assembly_dir);
DECLARE_string(boot_image);
DECLARE_string(system_image_dir);
@@ -337,6 +359,60 @@
return stream.str();
}
+std::optional<CuttlefishConfig::DisplayConfig> ParseDisplayConfig(
+ const std::string& flag) {
+ if (flag.empty()) {
+ return std::nullopt;
+ }
+
+ std::unordered_map<std::string, std::string> props;
+
+ const std::vector<std::string> pairs = android::base::Split(flag, ",");
+ for (const std::string& pair : pairs) {
+ const std::vector<std::string> keyvalue = android::base::Split(pair, "=");
+ CHECK_EQ(2, keyvalue.size()) << "Invalid display: " << flag;
+
+ const std::string& prop_key = keyvalue[0];
+ const std::string& prop_val = keyvalue[1];
+ props[prop_key] = prop_val;
+ }
+
+ CHECK(props.find("width") != props.end())
+ << "Display configuration missing 'width' in " << flag;
+ CHECK(props.find("height") != props.end())
+ << "Display configuration missing 'height' in " << flag;
+
+ int display_width;
+ CHECK(android::base::ParseInt(props["width"], &display_width))
+ << "Display configuration invalid 'width' in " << flag;
+
+ int display_height;
+ CHECK(android::base::ParseInt(props["height"], &display_height))
+ << "Display configuration invalid 'height' in " << flag;
+
+ int display_dpi = 320;
+ auto display_dpi_it = props.find("dpi");
+ if (display_dpi_it != props.end()) {
+ CHECK(android::base::ParseInt(display_dpi_it->second, &display_dpi))
+ << "Display configuration invalid 'dpi' in " << flag;
+ }
+
+ int display_refresh_rate_hz = 60;
+ auto display_refresh_rate_hz_it = props.find("refresh_rate_hz");
+ if (display_refresh_rate_hz_it != props.end()) {
+ CHECK(android::base::ParseInt(display_refresh_rate_hz_it->second,
+ &display_refresh_rate_hz))
+ << "Display configuration invalid 'refresh_rate_hz' in " << flag;
+ }
+
+ return CuttlefishConfig::DisplayConfig{
+ .width = display_width,
+ .height = display_height,
+ .dpi = display_dpi,
+ .refresh_rate_hz = display_refresh_rate_hz,
+ };
+}
+
#ifdef __ANDROID__
void ReadKernelConfig(KernelConfig* kernel_config) {
// QEMU isn't on Android, so always follow host arch
@@ -408,6 +484,40 @@
}
tmp_config_obj.set_vm_manager(FLAGS_vm_manager);
+ std::vector<CuttlefishConfig::DisplayConfig> display_configs;
+
+ auto display0 = ParseDisplayConfig(FLAGS_display0);
+ if (display0) {
+ display_configs.push_back(*display0);
+ }
+ auto display1 = ParseDisplayConfig(FLAGS_display1);
+ if (display1) {
+ display_configs.push_back(*display1);
+ }
+ auto display2 = ParseDisplayConfig(FLAGS_display2);
+ if (display2) {
+ display_configs.push_back(*display2);
+ }
+ auto display3 = ParseDisplayConfig(FLAGS_display3);
+ if (display3) {
+ display_configs.push_back(*display3);
+ }
+
+ if (FLAGS_x_res > 0 && FLAGS_y_res > 0) {
+ if (display_configs.empty()) {
+ display_configs.push_back({
+ .width = FLAGS_x_res,
+ .height = FLAGS_y_res,
+ .dpi = FLAGS_dpi,
+ .refresh_rate_hz = FLAGS_refresh_rate_hz,
+ });
+ } else {
+ LOG(WARNING) << "Ignoring --x_res and --y_res when --displayN specified.";
+ }
+ }
+
+ tmp_config_obj.set_display_configs(display_configs);
+
const GraphicsAvailability graphics_availability =
GetGraphicsAvailabilityWithSubprocessCheck();
@@ -423,8 +533,8 @@
}
if (tmp_config_obj.gpu_mode() == kGpuModeAuto) {
if (ShouldEnableAcceleratedRendering(graphics_availability)) {
- LOG(INFO) << "GPU auto mode: detected prerequisites for accelerated "
- "rendering support.";
+ LOG(INFO) << "GPU auto mode: detected prerequisites for accelerated "
+ "rendering support.";
if (FLAGS_vm_manager == QemuManager::name()) {
LOG(INFO) << "Enabling --gpu_mode=drm_virgl.";
tmp_config_obj.set_gpu_mode(kGpuModeDrmVirgl);
@@ -469,14 +579,6 @@
tmp_config_obj.set_setupwizard_mode(FLAGS_setupwizard_mode);
- std::vector<cuttlefish::CuttlefishConfig::DisplayConfig> display_configs = {{
- .width = FLAGS_x_res,
- .height = FLAGS_y_res,
- }};
- tmp_config_obj.set_display_configs(display_configs);
- tmp_config_obj.set_dpi(FLAGS_dpi);
- tmp_config_obj.set_refresh_rate_hz(FLAGS_refresh_rate_hz);
-
auto secure_hals = android::base::Split(FLAGS_secure_hals, ",");
tmp_config_obj.set_secure_hals(
std::set<std::string>(secure_hals.begin(), secure_hals.end()));
@@ -616,8 +718,6 @@
tmp_config_obj.set_record_screen(FLAGS_record_screen);
- tmp_config_obj.set_ethernet(FLAGS_ethernet);
-
tmp_config_obj.set_enable_host_bluetooth(FLAGS_enable_host_bluetooth);
tmp_config_obj.set_protected_vm(FLAGS_protected_vm);
@@ -701,6 +801,7 @@
instance.set_rootcanal_default_commands_file(
FLAGS_bluetooth_default_commands_file);
+ instance.set_camera_server_port(FLAGS_camera_server_port);
instance.set_device_title(FLAGS_device_title);
if (FLAGS_protected_vm) {
diff --git a/host/commands/kernel_log_monitor/kernel_log_server.cc b/host/commands/kernel_log_monitor/kernel_log_server.cc
index 66fafab..03fc90d 100644
--- a/host/commands/kernel_log_monitor/kernel_log_server.cc
+++ b/host/commands/kernel_log_monitor/kernel_log_server.cc
@@ -41,10 +41,13 @@
{cuttlefish::kMobileNetworkConnectedMessage,
monitor::Event::MobileNetworkConnected},
{cuttlefish::kWifiConnectedMessage, monitor::Event::WifiNetworkConnected},
- {cuttlefish::kEthernetConnectedMessage, monitor::Event::EthernetNetworkConnected},
+ {cuttlefish::kEthernetConnectedMessage,
+ monitor::Event::EthernetNetworkConnected},
// TODO(b/131864854): Replace this with a string less likely to change
{"init: starting service 'adbd'...", monitor::Event::AdbdStarted},
{cuttlefish::kScreenChangedMessage, monitor::Event::ScreenChanged},
+ {cuttlefish::kDisplayPowerModeChangedMessage,
+ monitor::Event::DisplayPowerModeChanged},
};
void ProcessSubscriptions(
diff --git a/host/commands/kernel_log_monitor/kernel_log_server.h b/host/commands/kernel_log_monitor/kernel_log_server.h
index 659a372..dab675d 100644
--- a/host/commands/kernel_log_monitor/kernel_log_server.h
+++ b/host/commands/kernel_log_monitor/kernel_log_server.h
@@ -37,6 +37,7 @@
AdbdStarted = 5,
ScreenChanged = 6,
EthernetNetworkConnected = 7,
+ DisplayPowerModeChanged = 9,
};
enum class SubscriptionAction {
diff --git a/host/commands/run_cvd/launch_streamer.cpp b/host/commands/run_cvd/launch_streamer.cpp
index bf0d77d..7ae3f1b 100644
--- a/host/commands/run_cvd/launch_streamer.cpp
+++ b/host/commands/run_cvd/launch_streamer.cpp
@@ -44,26 +44,37 @@
// Creates the frame and input sockets and add the relevant arguments to the vnc
// server and webrtc commands
void CreateStreamerServers(Command* cmd, const CuttlefishConfig& config) {
- SharedFD touch_server;
+ std::vector<SharedFD> touch_servers;
SharedFD keyboard_server;
auto instance = config.ForDefaultInstance();
- if (config.vm_manager() == vm_manager::QemuManager::name()) {
+ auto use_vsockets = config.vm_manager() == vm_manager::QemuManager::name();
+ for (int i = 0; i < config.display_configs().size(); ++i) {
+ touch_servers.push_back(
+ use_vsockets
+ ? SharedFD::VsockServer(instance.touch_server_port(), SOCK_STREAM)
+ : CreateUnixInputServer(instance.touch_socket_path(i)));
+ if (!touch_servers.back()->IsOpen()) {
+ LOG(ERROR) << "Could not open touch server: "
+ << touch_servers.back()->StrError();
+ return;
+ }
+ }
+ if (!touch_servers.empty()) {
+ cmd->AddParameter("-touch_fds=", touch_servers[0]);
+ for (int i = 1; i < touch_servers.size(); ++i) {
+ cmd->AppendToLastParameter(",", touch_servers[i]);
+ }
+ }
+
+ if (use_vsockets) {
cmd->AddParameter("-write_virtio_input");
- touch_server =
- SharedFD::VsockServer(instance.touch_server_port(), SOCK_STREAM);
keyboard_server =
SharedFD::VsockServer(instance.keyboard_server_port(), SOCK_STREAM);
} else {
- touch_server = CreateUnixInputServer(instance.touch_socket_path());
keyboard_server = CreateUnixInputServer(instance.keyboard_socket_path());
}
- if (!touch_server->IsOpen()) {
- LOG(ERROR) << "Could not open touch server: " << touch_server->StrError();
- return;
- }
- cmd->AddParameter("-touch_fd=", touch_server);
if (!keyboard_server->IsOpen()) {
LOG(ERROR) << "Could not open keyboard server: "
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index f40226a..944f76c 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -238,8 +238,7 @@
} else if (used_tap_devices.count(instance.mobile_tap_name())) {
LOG(ERROR) << "Mobile TAP device already in use";
return RunnerExitCodes::kTapDeviceInUse;
- } else if (config->ethernet() &&
- used_tap_devices.count(instance.ethernet_tap_name())) {
+ } else if (used_tap_devices.count(instance.ethernet_tap_name())) {
LOG(ERROR) << "Ethernet TAP device already in use";
}
diff --git a/host/commands/secure_env/tpm_attestation_record.cpp b/host/commands/secure_env/tpm_attestation_record.cpp
index f47a0ef..75a2920 100644
--- a/host/commands/secure_env/tpm_attestation_record.cpp
+++ b/host/commands/secure_env/tpm_attestation_record.cpp
@@ -16,11 +16,20 @@
#include "host/commands/secure_env/tpm_attestation_record.h"
#include <keymaster/contexts/soft_attestation_cert.h>
+#include <keymaster/km_openssl/attestation_record.h>
+
+#include <openssl/rand.h>
#include <android-base/logging.h>
using keymaster::AuthorizationSet;
+TpmAttestationRecordContext::TpmAttestationRecordContext()
+ : keymaster::AttestationContext(::keymaster::KmVersion::KEYMINT_1),
+ unique_id_hbk_(16) {
+ RAND_bytes(unique_id_hbk_.data(), unique_id_hbk_.size());
+}
+
keymaster_security_level_t TpmAttestationRecordContext::GetSecurityLevel() const {
return KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
}
@@ -38,10 +47,11 @@
}
keymaster::Buffer TpmAttestationRecordContext::GenerateUniqueId(
- uint64_t, const keymaster_blob_t&, bool, keymaster_error_t* error) const {
- LOG(ERROR) << "TODO(schuffelen): Implement GenerateUniqueId";
- *error = KM_ERROR_UNIMPLEMENTED;
- return {};
+ uint64_t creation_date_time, const keymaster_blob_t& application_id,
+ bool reset_since_rotation, keymaster_error_t* error) const {
+ *error = KM_ERROR_OK;
+ return keymaster::generate_unique_id(unique_id_hbk_, creation_date_time,
+ application_id, reset_since_rotation);
}
const keymaster::AttestationContext::VerifiedBootParams*
diff --git a/host/commands/secure_env/tpm_attestation_record.h b/host/commands/secure_env/tpm_attestation_record.h
index 01e31d7..1609351 100644
--- a/host/commands/secure_env/tpm_attestation_record.h
+++ b/host/commands/secure_env/tpm_attestation_record.h
@@ -16,13 +16,13 @@
#pragma once
#include <memory>
+#include <vector>
#include <keymaster/attestation_context.h>
class TpmAttestationRecordContext : public keymaster::AttestationContext {
public:
- TpmAttestationRecordContext()
- : keymaster::AttestationContext(::keymaster::KmVersion::KEYMINT_1) {}
+ TpmAttestationRecordContext();
~TpmAttestationRecordContext() = default;
keymaster_security_level_t GetSecurityLevel() const override;
@@ -40,4 +40,5 @@
private:
mutable std::unique_ptr<VerifiedBootParams> vb_params_;
+ std::vector<uint8_t> unique_id_hbk_;
};
diff --git a/host/frontend/vnc_server/Android.bp b/host/frontend/vnc_server/Android.bp
index bfb5561..468590b 100644
--- a/host/frontend/vnc_server/Android.bp
+++ b/host/frontend/vnc_server/Android.bp
@@ -51,8 +51,9 @@
"libffi",
"libjpeg",
"libgflags",
- "libwayland_server",
+ "libwayland_crosvm_gpu_display_extension_server_protocols",
"libwayland_extension_server_protocols",
+ "libwayland_server",
],
defaults: ["cuttlefish_host"],
}
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index 15bcd8b..a02ae4b 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -73,7 +73,9 @@
SimulatedHWComposer::GenerateProcessedFrameCallback
SimulatedHWComposer::GetScreenConnectorCallback() {
- return [](std::uint32_t display_number, std::uint8_t* frame_pixels,
+ return [](std::uint32_t display_number, std::uint32_t frame_w,
+ std::uint32_t frame_h, std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_bytes,
cuttlefish::vnc::VncScProcessedFrame& processed_frame) {
processed_frame.display_number_ = display_number;
// TODO(171305898): handle multiple displays.
@@ -86,32 +88,25 @@
// here is incorrectly assuming surface id == display id.
display_number = 0;
}
- const std::uint32_t display_w =
- ScreenConnector::ScreenWidth(display_number);
- const std::uint32_t display_h =
- ScreenConnector::ScreenHeight(display_number);
- const std::uint32_t display_stride_bytes =
- ScreenConnector::ScreenStrideBytes(display_number);
- const std::uint32_t display_bpp = ScreenConnector::BytesPerPixel();
- const std::uint32_t display_size_bytes =
- ScreenConnector::ScreenSizeInBytes(display_number);
+
+ const std::uint32_t frame_bpp = 4;
+ const std::uint32_t frame_size_bytes = frame_h * frame_stride_bytes;
auto& raw_screen = processed_frame.raw_screen_;
- raw_screen.assign(frame_pixels, frame_pixels + display_size_bytes);
+ raw_screen.assign(frame_bytes, frame_bytes + frame_size_bytes);
static std::uint32_t next_frame_number = 0;
const auto num_stripes = SimulatedHWComposer::kNumStripes;
for (int i = 0; i < num_stripes; ++i) {
- std::uint16_t y = (display_h / num_stripes) * i;
+ std::uint16_t y = (frame_h / num_stripes) * i;
// Last frames on the right and/or bottom handle extra pixels
// when a screen dimension is not evenly divisible by Frame::kNumSlots.
- std::uint16_t height =
- display_h / num_stripes +
- (i + 1 == num_stripes ? display_h % num_stripes : 0);
- const auto* raw_start = &raw_screen[y * display_w * display_bpp];
- const auto* raw_end = raw_start + (height * display_w * display_bpp);
+ std::uint16_t height = frame_h / num_stripes +
+ (i + 1 == num_stripes ? frame_h % num_stripes : 0);
+ const auto* raw_start = &raw_screen[y * frame_w * frame_bpp];
+ const auto* raw_end = raw_start + (height * frame_w * frame_bpp);
// creating a named object and setting individual data members in order
// to make klp happy
// TODO (haining) construct this inside the call when not compiling
@@ -120,8 +115,8 @@
s.index = i;
s.x = 0;
s.y = y;
- s.width = display_w;
- s.stride = display_stride_bytes;
+ s.width = frame_w;
+ s.stride = frame_stride_bytes;
s.height = height;
s.frame_id = next_frame_number++;
s.raw_data.assign(raw_start, raw_end);
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
index 35fda80..4876338 100644
--- a/host/frontend/vnc_server/virtual_inputs.cpp
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -16,8 +16,9 @@
#include "host/frontend/vnc_server/virtual_inputs.h"
-#include <gflags/gflags.h>
#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <gflags/gflags.h>
#include <linux/input.h>
#include <linux/uinput.h>
@@ -33,8 +34,8 @@
using cuttlefish::vnc::VirtualInputs;
-DEFINE_int32(touch_fd, -1,
- "A fd for a socket where to accept touch connections");
+DEFINE_string(touch_fds, "",
+ "A list of fds for sockets where to accept touch connections");
DEFINE_int32(keyboard_fd, -1,
"A fd for a socket where to accept keyboard connections");
@@ -315,9 +316,10 @@
}
void ClientConnectorLoop() {
- auto touch_server = cuttlefish::SharedFD::Dup(FLAGS_touch_fd);
- close(FLAGS_touch_fd);
- FLAGS_touch_fd = -1;
+ auto touch_fd =
+ std::stoi(android::base::Split(FLAGS_touch_fds, ",").front());
+ auto touch_server = cuttlefish::SharedFD::Dup(touch_fd);
+ close(touch_fd);
auto keyboard_server = cuttlefish::SharedFD::Dup(FLAGS_keyboard_fd);
close(FLAGS_keyboard_fd);
diff --git a/host/frontend/webrtc/Android.bp b/host/frontend/webrtc/Android.bp
index f03a589..42f7fa4 100644
--- a/host/frontend/webrtc/Android.bp
+++ b/host/frontend/webrtc/Android.bp
@@ -22,6 +22,7 @@
srcs: [
"lib/audio_device.cpp",
"lib/audio_track_source_impl.cpp",
+ "lib/camera_streamer.cpp",
"lib/client_handler.cpp",
"lib/keyboard.cpp",
"lib/local_recorder.cpp",
@@ -50,8 +51,9 @@
"libgflags",
"libdrm",
"libffi",
- "libwayland_server",
+ "libwayland_crosvm_gpu_display_extension_server_protocols",
"libwayland_extension_server_protocols",
+ "libwayland_server",
"libwebsockets",
"libcap",
"libcuttlefish_utils",
@@ -117,6 +119,7 @@
"libopus",
"libsrtp2",
"libvpx",
+ "libwayland_crosvm_gpu_display_extension_server_protocols",
"libwayland_extension_server_protocols",
"libwayland_server",
"libwebrtc",
diff --git a/host/frontend/webrtc/audio_handler.cpp b/host/frontend/webrtc/audio_handler.cpp
index 4046995..1cd8938 100644
--- a/host/frontend/webrtc/audio_handler.cpp
+++ b/host/frontend/webrtc/audio_handler.cpp
@@ -25,6 +25,28 @@
namespace cuttlefish {
namespace {
+const virtio_snd_jack_info JACKS[] = {};
+constexpr uint32_t NUM_JACKS = sizeof(JACKS) / sizeof(JACKS[0]);
+
+const virtio_snd_chmap_info CHMAPS[] = {{
+ .hdr = { .hda_fn_nid = Le32(0), },
+ .direction = (uint8_t) AudioStreamDirection::VIRTIO_SND_D_OUTPUT,
+ .channels = 2,
+ .positions = {
+ (uint8_t) AudioChannelMap::VIRTIO_SND_CHMAP_FL,
+ (uint8_t) AudioChannelMap::VIRTIO_SND_CHMAP_FR
+ },
+}, {
+ .hdr = { .hda_fn_nid = Le32(0), },
+ .direction = (uint8_t) AudioStreamDirection::VIRTIO_SND_D_INPUT,
+ .channels = 2,
+ .positions = {
+ (uint8_t) AudioChannelMap::VIRTIO_SND_CHMAP_FL,
+ (uint8_t) AudioChannelMap::VIRTIO_SND_CHMAP_FR
+ },
+}};
+constexpr uint32_t NUM_CHMAPS = sizeof(CHMAPS) / sizeof(CHMAPS[0]);
+
const virtio_snd_pcm_info STREAMS[] = {{
.hdr =
{
@@ -35,10 +57,10 @@
// formats: It only takes the bits_per_sample as a parameter and assumes
// the underlying format to be one of the following:
.formats = Le64(
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U8) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U16) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U24) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U32)),
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S8) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S16) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S24) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S32)),
.rates = Le64(
(((uint64_t)1) << (uint8_t)AudioStreamRate::VIRTIO_SND_PCM_RATE_5512) |
(((uint64_t)1) << (uint8_t)AudioStreamRate::VIRTIO_SND_PCM_RATE_8000) |
@@ -70,10 +92,10 @@
// formats: It only takes the bits_per_sample as a parameter and assumes
// the underlying format to be one of the following:
.formats = Le64(
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U8) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U16) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U24) |
- (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_U32)),
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S8) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S16) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S24) |
+ (((uint64_t)1) << (uint8_t)AudioStreamFormat::VIRTIO_SND_PCM_FMT_S32)),
.rates = Le64(
(((uint64_t)1) << (uint8_t)AudioStreamRate::VIRTIO_SND_PCM_RATE_5512) |
(((uint64_t)1) << (uint8_t)AudioStreamRate::VIRTIO_SND_PCM_RATE_8000) |
@@ -271,7 +293,7 @@
[[noreturn]] void AudioHandler::Loop() {
for (;;) {
auto audio_client = audio_server_->AcceptClient(
- NUM_STREAMS, 0 /* num_jacks, */, 0 /* num_chmaps, */,
+ NUM_STREAMS, NUM_JACKS, NUM_CHMAPS,
262144 /* tx_shm_len */, 262144 /* rx_shm_len */);
CHECK(audio_client) << "Failed to create audio client connection instance";
@@ -362,6 +384,28 @@
cmd.Reply(AudioStatus::VIRTIO_SND_S_OK);
}
+void AudioHandler::ChmapsInfo(ChmapInfoCommand& cmd) {
+ if (cmd.start_id() >= NUM_CHMAPS ||
+ cmd.start_id() + cmd.count() > NUM_CHMAPS) {
+ cmd.Reply(AudioStatus::VIRTIO_SND_S_BAD_MSG, {});
+ return;
+ }
+ std::vector<virtio_snd_chmap_info> chmap_info(
+ &CHMAPS[cmd.start_id()], &CHMAPS[cmd.start_id()] + cmd.count());
+ cmd.Reply(AudioStatus::VIRTIO_SND_S_OK, chmap_info);
+}
+
+void AudioHandler::JacksInfo(JackInfoCommand& cmd) {
+ if (cmd.start_id() >= NUM_JACKS ||
+ cmd.start_id() + cmd.count() > NUM_JACKS) {
+ cmd.Reply(AudioStatus::VIRTIO_SND_S_BAD_MSG, {});
+ return;
+ }
+ std::vector<virtio_snd_jack_info> jack_info(
+ &JACKS[cmd.start_id()], &JACKS[cmd.start_id()] + cmd.count());
+ cmd.Reply(AudioStatus::VIRTIO_SND_S_OK, jack_info);
+}
+
void AudioHandler::OnPlaybackBuffer(TxBuffer buffer) {
auto stream_id = buffer.stream_id();
auto& stream_desc = stream_descs_[stream_id];
diff --git a/host/frontend/webrtc/audio_handler.h b/host/frontend/webrtc/audio_handler.h
index 3802052..4da645c 100644
--- a/host/frontend/webrtc/audio_handler.h
+++ b/host/frontend/webrtc/audio_handler.h
@@ -63,6 +63,8 @@
void ReleaseStream(StreamControlCommand& cmd) override;
void StartStream(StreamControlCommand& cmd) override;
void StopStream(StreamControlCommand& cmd) override;
+ void ChmapsInfo(ChmapInfoCommand& cmd) override;
+ void JacksInfo(JackInfoCommand& cmd) override;
void OnPlaybackBuffer(TxBuffer buffer) override;
void OnCaptureBuffer(RxBuffer buffer) override;
diff --git a/host/frontend/webrtc/connection_observer.cpp b/host/frontend/webrtc/connection_observer.cpp
index 9f11544..0975faa 100644
--- a/host/frontend/webrtc/connection_observer.cpp
+++ b/host/frontend/webrtc/connection_observer.cpp
@@ -35,6 +35,7 @@
#include "common/libs/fs/shared_buf.h"
#include "host/frontend/webrtc/adb_handler.h"
#include "host/frontend/webrtc/bluetooth_handler.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
#include "host/frontend/webrtc/lib/utils.h"
#include "host/libs/config/cuttlefish_config.h"
@@ -103,11 +104,13 @@
cuttlefish::KernelLogEventsHandler *kernel_log_events_handler,
std::map<std::string, cuttlefish::SharedFD>
commands_to_custom_action_servers,
- std::weak_ptr<DisplayHandler> display_handler)
+ std::weak_ptr<DisplayHandler> display_handler,
+ CameraController *camera_controller)
: input_sockets_(input_sockets),
kernel_log_events_handler_(kernel_log_events_handler),
commands_to_custom_action_servers_(commands_to_custom_action_servers),
- weak_display_handler_(display_handler) {}
+ weak_display_handler_(display_handler),
+ camera_controller_(camera_controller) {}
virtual ~ConnectionObserverForAndroid() {
auto display_handler = weak_display_handler_.lock();
if (display_handler) {
@@ -141,67 +144,65 @@
}
}
- void OnTouchEvent(const std::string & /*display_label*/, int x, int y,
- bool down) override {
-
- auto buffer = GetEventBuffer();
- if (!buffer) {
- LOG(ERROR) << "Failed to allocate event buffer";
- return;
- }
- buffer->AddEvent(EV_ABS, ABS_X, x);
- buffer->AddEvent(EV_ABS, ABS_Y, y);
- buffer->AddEvent(EV_KEY, BTN_TOUCH, down);
- buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
- cuttlefish::WriteAll(input_sockets_.touch_client,
- reinterpret_cast<const char *>(buffer->data()),
- buffer->size());
- }
-
- void OnMultiTouchEvent(const std::string & /*display_label*/, Json::Value id,
- Json::Value slot, Json::Value x, Json::Value y,
- bool down, int size) override {
-
- auto buffer = GetEventBuffer();
- if (!buffer) {
- LOG(ERROR) << "Failed to allocate event buffer";
- return;
+ void OnTouchEvent(const std::string &display_label, int x, int y,
+ bool down) override {
+ auto buffer = GetEventBuffer();
+ if (!buffer) {
+ LOG(ERROR) << "Failed to allocate event buffer";
+ return;
+ }
+ buffer->AddEvent(EV_ABS, ABS_X, x);
+ buffer->AddEvent(EV_ABS, ABS_Y, y);
+ buffer->AddEvent(EV_KEY, BTN_TOUCH, down);
+ buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
+ cuttlefish::WriteAll(input_sockets_.GetTouchClientByLabel(display_label),
+ reinterpret_cast<const char *>(buffer->data()),
+ buffer->size());
}
- for (int i=0; i<size; i++) {
- auto this_slot = slot[i].asInt();
- auto this_id = id[i].asInt();
- auto this_x = x[i].asInt();
- auto this_y = y[i].asInt();
- buffer->AddEvent(EV_ABS, ABS_MT_SLOT, this_slot);
- if (down) {
- bool is_new = active_touch_slots_.insert(this_slot).second;
- if (is_new) {
+ void OnMultiTouchEvent(const std::string &display_label, Json::Value id,
+ Json::Value slot, Json::Value x, Json::Value y,
+ bool down, int size) override {
+ auto buffer = GetEventBuffer();
+ if (!buffer) {
+ LOG(ERROR) << "Failed to allocate event buffer";
+ return;
+ }
+
+ for (int i = 0; i < size; i++) {
+ auto this_slot = slot[i].asInt();
+ auto this_id = id[i].asInt();
+ auto this_x = x[i].asInt();
+ auto this_y = y[i].asInt();
+ buffer->AddEvent(EV_ABS, ABS_MT_SLOT, this_slot);
+ if (down) {
+ bool is_new = active_touch_slots_.insert(this_slot).second;
+ if (is_new) {
+ buffer->AddEvent(EV_ABS, ABS_MT_TRACKING_ID, this_id);
+ if (active_touch_slots_.size() == 1) {
+ buffer->AddEvent(EV_KEY, BTN_TOUCH, 1);
+ }
+ }
+ buffer->AddEvent(EV_ABS, ABS_MT_POSITION_X, this_x);
+ buffer->AddEvent(EV_ABS, ABS_MT_POSITION_Y, this_y);
+ // send ABS_X and ABS_Y for single-touch compatibility
+ buffer->AddEvent(EV_ABS, ABS_X, this_x);
+ buffer->AddEvent(EV_ABS, ABS_Y, this_y);
+ } else {
+ // released touch
buffer->AddEvent(EV_ABS, ABS_MT_TRACKING_ID, this_id);
- if (active_touch_slots_.size() == 1) {
- buffer->AddEvent(EV_KEY, BTN_TOUCH, 1);
+ active_touch_slots_.erase(this_slot);
+ if (active_touch_slots_.empty()) {
+ buffer->AddEvent(EV_KEY, BTN_TOUCH, 0);
}
}
- buffer->AddEvent(EV_ABS, ABS_MT_POSITION_X, this_x);
- buffer->AddEvent(EV_ABS, ABS_MT_POSITION_Y, this_y);
- // send ABS_X and ABS_Y for single-touch compatibility
- buffer->AddEvent(EV_ABS, ABS_X, this_x);
- buffer->AddEvent(EV_ABS, ABS_Y, this_y);
- } else {
- // released touch
- buffer->AddEvent(EV_ABS, ABS_MT_TRACKING_ID, this_id);
- active_touch_slots_.erase(this_slot);
- if (active_touch_slots_.empty()) {
- buffer->AddEvent(EV_KEY, BTN_TOUCH, 0);
- }
}
- }
- buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
- cuttlefish::WriteAll(input_sockets_.touch_client,
- reinterpret_cast<const char *>(buffer->data()),
- buffer->size());
- }
+ buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
+ cuttlefish::WriteAll(input_sockets_.GetTouchClientByLabel(display_label),
+ reinterpret_cast<const char *>(buffer->data()),
+ buffer->size());
+ }
void OnKeyboardEvent(uint16_t code, bool down) override {
auto buffer = GetEventBuffer();
@@ -244,6 +245,9 @@
void OnControlChannelOpen(std::function<bool(const Json::Value)>
control_message_sender) override {
LOG(VERBOSE) << "Control Channel open";
+ if (camera_controller_) {
+ camera_controller_->SetMessageSender(control_message_sender);
+ }
kernel_log_subscription_id_ =
kernel_log_events_handler_->AddSubscriber(control_message_sender);
}
@@ -284,6 +288,10 @@
// Sensor HAL.
}
return;
+ } else if (command.rfind("camera_", 0) == 0 && camera_controller_) {
+ // Handle commands starting with "camera_" by camera controller
+ camera_controller_->HandleMessage(evt);
+ return;
}
auto button_state = evt["button_state"].asString();
@@ -330,6 +338,12 @@
bluetooth_handler_->handleMessage(msg, size);
}
+ void OnCameraData(const std::vector<char> &data) override {
+ if (camera_controller_) {
+ camera_controller_->HandleMessage(data);
+ }
+ }
+
private:
cuttlefish::InputSockets& input_sockets_;
cuttlefish::KernelLogEventsHandler* kernel_log_events_handler_;
@@ -340,6 +354,7 @@
std::map<std::string, cuttlefish::SharedFD> commands_to_custom_action_servers_;
std::weak_ptr<DisplayHandler> weak_display_handler_;
std::set<int32_t> active_touch_slots_;
+ cuttlefish::CameraController *camera_controller_;
};
class ConnectionObserverDemuxer
@@ -352,10 +367,12 @@
std::map<std::string, cuttlefish::SharedFD>
commands_to_custom_action_servers,
std::weak_ptr<DisplayHandler> display_handler,
+ CameraController *camera_controller,
/* params for this class */
cuttlefish::confui::HostVirtualInput &confui_input)
: android_input_(input_sockets, kernel_log_events_handler,
- commands_to_custom_action_servers, display_handler),
+ commands_to_custom_action_servers, display_handler,
+ camera_controller),
confui_input_{confui_input} {}
virtual ~ConnectionObserverDemuxer() = default;
@@ -433,6 +450,10 @@
android_input_.OnBluetoothMessage(msg, size);
}
+ void OnCameraData(const std::vector<char> &data) override {
+ android_input_.OnCameraData(data);
+ }
+
private:
ConnectionObserverForAndroid android_input_;
cuttlefish::confui::HostVirtualInput &confui_input_;
@@ -451,7 +472,8 @@
return std::shared_ptr<cuttlefish::webrtc_streaming::ConnectionObserver>(
new ConnectionObserverDemuxer(input_sockets_, kernel_log_events_handler_,
commands_to_custom_action_servers_,
- weak_display_handler_, confui_input_));
+ weak_display_handler_, camera_controller_,
+ confui_input_));
}
void CfConnectionObserverFactory::AddCustomActionServer(
@@ -467,4 +489,9 @@
std::weak_ptr<DisplayHandler> display_handler) {
weak_display_handler_ = display_handler;
}
+
+void CfConnectionObserverFactory::SetCameraHandler(
+ CameraController *controller) {
+ camera_controller_ = controller;
+}
} // namespace cuttlefish
diff --git a/host/frontend/webrtc/connection_observer.h b/host/frontend/webrtc/connection_observer.h
index abf1884..f31472c 100644
--- a/host/frontend/webrtc/connection_observer.h
+++ b/host/frontend/webrtc/connection_observer.h
@@ -22,14 +22,21 @@
#include "common/libs/fs/shared_fd.h"
#include "host/frontend/webrtc/display_handler.h"
#include "host/frontend/webrtc/kernel_log_events_handler.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
#include "host/frontend/webrtc/lib/connection_observer.h"
#include "host/libs/confui/host_virtual_input.h"
namespace cuttlefish {
struct InputSockets {
- SharedFD touch_server;
- SharedFD touch_client;
+ SharedFD GetTouchClientByLabel(const std::string& label) {
+ return touch_clients[label];
+ }
+
+ // TODO (b/186773052): Finding strings in a map for every input event may
+ // introduce unwanted latency.
+ std::map<std::string, SharedFD> touch_servers;
+ std::map<std::string, SharedFD> touch_clients;
SharedFD keyboard_server;
SharedFD keyboard_client;
SharedFD switches_server;
@@ -53,6 +60,8 @@
void SetDisplayHandler(std::weak_ptr<DisplayHandler> display_handler);
+ void SetCameraHandler(CameraController* controller);
+
private:
InputSockets& input_sockets_;
KernelLogEventsHandler* kernel_log_events_handler_;
@@ -60,6 +69,7 @@
commands_to_custom_action_servers_;
std::weak_ptr<DisplayHandler> weak_display_handler_;
cuttlefish::confui::HostVirtualInput& confui_input_;
+ cuttlefish::CameraController* camera_controller_ = nullptr;
};
} // namespace cuttlefish
diff --git a/host/frontend/webrtc/display_handler.cpp b/host/frontend/webrtc/display_handler.cpp
index 1b3bfd8..334c35f 100644
--- a/host/frontend/webrtc/display_handler.cpp
+++ b/host/frontend/webrtc/display_handler.cpp
@@ -24,42 +24,27 @@
namespace cuttlefish {
DisplayHandler::DisplayHandler(
- std::shared_ptr<webrtc_streaming::VideoSink> display_sink,
+ std::vector<std::shared_ptr<webrtc_streaming::VideoSink>> display_sinks,
ScreenConnector& screen_connector)
- : display_sink_(display_sink), screen_connector_(screen_connector) {
+ : display_sinks_(display_sinks), screen_connector_(screen_connector) {
screen_connector_.SetCallback(std::move(GetScreenConnectorCallback()));
}
DisplayHandler::GenerateProcessedFrameCallback DisplayHandler::GetScreenConnectorCallback() {
// only to tell the producer how to create a ProcessedFrame to cache into the queue
DisplayHandler::GenerateProcessedFrameCallback callback =
- [](std::uint32_t display_number, std::uint8_t* frame_pixels,
+ [](std::uint32_t display_number, std::uint32_t frame_width,
+ std::uint32_t frame_height, std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_pixels,
WebRtcScProcessedFrame& processed_frame) {
processed_frame.display_number_ = display_number;
- // TODO(171305898): handle multiple displays.
- if (display_number != 0) {
- // BUG 186580833: display_number comes from surface_id in crosvm
- // create_surface from virtio_gpu.rs set_scanout. We cannot use it as
- // the display number. Either crosvm virtio-gpu is incorrectly ignoring
- // scanout id and instead using a monotonically increasing surface id
- // number as the scanout resource is replaced over time, or frontend code
- // here is incorrectly assuming surface id == display id.
- display_number = 0;
- }
- const int display_w =
- ScreenConnectorInfo::ScreenWidth(display_number);
- const int display_h =
- ScreenConnectorInfo::ScreenHeight(display_number);
- const int display_stride_bytes =
- ScreenConnectorInfo::ScreenStrideBytes(display_number);
- processed_frame.display_number_ = display_number;
processed_frame.buf_ =
- std::make_unique<CvdVideoFrameBuffer>(display_w, display_h);
+ std::make_unique<CvdVideoFrameBuffer>(frame_width, frame_height);
libyuv::ABGRToI420(
- frame_pixels, display_stride_bytes, processed_frame.buf_->DataY(),
+ frame_pixels, frame_stride_bytes, processed_frame.buf_->DataY(),
processed_frame.buf_->StrideY(), processed_frame.buf_->DataU(),
processed_frame.buf_->StrideU(), processed_frame.buf_->DataV(),
- processed_frame.buf_->StrideV(), display_w, display_h);
+ processed_frame.buf_->StrideV(), frame_width, frame_height);
processed_frame.is_success_ = true;
};
return callback;
@@ -72,6 +57,7 @@
{
std::lock_guard<std::mutex> lock(last_buffer_mutex_);
std::shared_ptr<CvdVideoFrameBuffer> buffer = std::move(processed_frame.buf_);
+ last_buffer_display_ = processed_frame.display_number_;
last_buffer_ =
std::static_pointer_cast<webrtc_streaming::VideoFrameBuffer>(buffer);
}
@@ -83,9 +69,11 @@
void DisplayHandler::SendLastFrame() {
std::shared_ptr<webrtc_streaming::VideoFrameBuffer> buffer;
+ std::uint32_t buffer_display;
{
std::lock_guard<std::mutex> lock(last_buffer_mutex_);
buffer = last_buffer_;
+ buffer_display = last_buffer_display_;
}
if (!buffer) {
// If a connection request arrives before the first frame is available don't
@@ -100,7 +88,7 @@
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
- display_sink_->OnFrame(buffer, time_stamp);
+ display_sinks_[buffer_display]->OnFrame(buffer, time_stamp);
}
}
diff --git a/host/frontend/webrtc/display_handler.h b/host/frontend/webrtc/display_handler.h
index aed38a1..7703b08 100644
--- a/host/frontend/webrtc/display_handler.h
+++ b/host/frontend/webrtc/display_handler.h
@@ -16,8 +16,9 @@
#pragma once
-#include <mutex>
#include <memory>
+#include <mutex>
+#include <vector>
#include "host/frontend/webrtc/cvd_video_frame_buffer.h"
#include "host/frontend/webrtc/lib/video_sink.h"
@@ -51,8 +52,9 @@
using ScreenConnector = cuttlefish::ScreenConnector<WebRtcScProcessedFrame>;
using GenerateProcessedFrameCallback = ScreenConnector::GenerateProcessedFrameCallback;
- DisplayHandler(std::shared_ptr<webrtc_streaming::VideoSink> display_sink,
- ScreenConnector& screen_connector);
+ DisplayHandler(
+ std::vector<std::shared_ptr<webrtc_streaming::VideoSink>> display_sinks,
+ ScreenConnector& screen_connector);
~DisplayHandler() = default;
[[noreturn]] void Loop();
@@ -63,9 +65,10 @@
private:
GenerateProcessedFrameCallback GetScreenConnectorCallback();
- std::shared_ptr<webrtc_streaming::VideoSink> display_sink_;
+ std::vector<std::shared_ptr<webrtc_streaming::VideoSink>> display_sinks_;
ScreenConnector& screen_connector_;
std::shared_ptr<webrtc_streaming::VideoFrameBuffer> last_buffer_;
+ std::uint32_t last_buffer_display_ = 0;
std::mutex last_buffer_mutex_;
std::mutex next_frame_mutex_;
int client_count_ = 0;
diff --git a/host/frontend/webrtc/kernel_log_events_handler.cpp b/host/frontend/webrtc/kernel_log_events_handler.cpp
index ca14e6f..e31e4da 100644
--- a/host/frontend/webrtc/kernel_log_events_handler.cpp
+++ b/host/frontend/webrtc/kernel_log_events_handler.cpp
@@ -86,6 +86,12 @@
message["metadata"] = read_result->metadata;
DeliverEvent(message);
}
+ if (read_result->event == monitor::Event::DisplayPowerModeChanged) {
+ Json::Value message;
+ message["event"] = kDisplayPowerModeChangedMessage;
+ message["metadata"] = read_result->metadata;
+ DeliverEvent(message);
+ }
}
}
}
diff --git a/host/frontend/webrtc/lib/camera_controller.h b/host/frontend/webrtc/lib/camera_controller.h
new file mode 100644
index 0000000..403af5b
--- /dev/null
+++ b/host/frontend/webrtc/lib/camera_controller.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <json/json.h>
+
+namespace cuttlefish {
+
+class CameraController {
+ public:
+ virtual ~CameraController() = default;
+
+ // Handle data messages coming from the client
+ virtual void HandleMessage(const std::vector<char>& message) = 0;
+ // Handle control messages coming from the client
+ virtual void HandleMessage(const Json::Value& message) = 0;
+ // Send control messages to client
+ virtual void SendMessage(const Json::Value& msg) {
+ if (message_sender_) {
+ message_sender_(msg);
+ }
+ }
+ virtual void SetMessageSender(
+ std::function<bool(const Json::Value& msg)> sender) {
+ message_sender_ = sender;
+ }
+
+ protected:
+ std::function<bool(const Json::Value& msg)> message_sender_;
+};
+
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/camera_streamer.cpp b/host/frontend/webrtc/lib/camera_streamer.cpp
new file mode 100644
index 0000000..7634e28
--- /dev/null
+++ b/host/frontend/webrtc/lib/camera_streamer.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "camera_streamer.h"
+
+#include <android-base/logging.h>
+#include <chrono>
+#include "common/libs/utils/vsock_connection.h"
+
+namespace cuttlefish {
+namespace webrtc_streaming {
+
+CameraStreamer::CameraStreamer(unsigned int port, unsigned int cid)
+ : cid_(cid), port_(port) {}
+
+CameraStreamer::~CameraStreamer() { Disconnect(); }
+
+// We are getting frames from the client so try forwarding those to the CVD
+void CameraStreamer::OnFrame(const webrtc::VideoFrame& client_frame) {
+ std::lock_guard<std::mutex> lock(onframe_mutex_);
+ if (!cvd_connection_.IsConnected() && !pending_connection_.valid()) {
+ // Start new connection
+ pending_connection_ = cvd_connection_.ConnectAsync(port_, cid_);
+ return;
+ } else if (pending_connection_.valid()) {
+ if (!IsConnectionReady()) {
+ return;
+ }
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ if (!cvd_connection_.WriteMessage(settings_buffer_)) {
+ LOG(ERROR) << "Failed writing camera settings:";
+ return;
+ }
+ StartReadLoop();
+ LOG(INFO) << "Connected!";
+ }
+ auto resolution = resolution_.load();
+ if (resolution.height <= 0 || resolution.width <= 0) {
+ // We don't have a valid resolution that is necessary for
+ // potential frame scaling
+ return;
+ }
+ auto frame = client_frame.video_frame_buffer()->ToI420().get();
+ if (frame->width() != resolution.width ||
+ frame->height() != resolution.height) {
+ // incoming resolution does not match with the resolution we
+ // have communicated to the CVD - scaling required
+ if (!scaled_frame_ || resolution.width != scaled_frame_->width() ||
+ resolution.height != scaled_frame_->height()) {
+ scaled_frame_ =
+ webrtc::I420Buffer::Create(resolution.width, resolution.height);
+ }
+ scaled_frame_->CropAndScaleFrom(*frame);
+ frame = scaled_frame_.get();
+ }
+ if (!VsockSendYUVFrame(frame)) {
+ LOG(ERROR) << "Sending frame over vsock failed";
+ }
+}
+
+// Handle message json coming from client
+void CameraStreamer::HandleMessage(const Json::Value& message) {
+ auto command = message["command"].asString();
+ if (command == "camera_settings") {
+ // save local copy of resolution that is required for frame scaling
+ resolution_ = GetResolutionFromSettings(message);
+ Json::StreamWriterBuilder factory;
+ std::string new_settings = Json::writeString(factory, message);
+ if (!settings_buffer_.empty() && new_settings != settings_buffer_) {
+ // Settings have changed - disconnect
+ // Next incoming frames will trigger re-connection
+ Disconnect();
+ }
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ settings_buffer_ = new_settings;
+ LOG(INFO) << "New camera settings received:" << new_settings;
+ }
+}
+
+// Handle binary blobs coming from client
+void CameraStreamer::HandleMessage(const std::vector<char>& message) {
+ LOG(INFO) << "Pass through " << message.size() << "bytes";
+ std::lock_guard<std::mutex> lock(frame_mutex_);
+ cvd_connection_.WriteMessage(message);
+}
+
+CameraStreamer::Resolution CameraStreamer::GetResolutionFromSettings(
+ const Json::Value& settings) {
+ return {.width = settings["width"].asInt(),
+ .height = settings["height"].asInt()};
+}
+
+bool CameraStreamer::VsockSendYUVFrame(
+ const webrtc::I420BufferInterface* frame) {
+ int32_t size = frame->width() * frame->height() +
+ 2 * frame->ChromaWidth() * frame->ChromaHeight();
+ const char* y = reinterpret_cast<const char*>(frame->DataY());
+ const char* u = reinterpret_cast<const char*>(frame->DataU());
+ const char* v = reinterpret_cast<const char*>(frame->DataV());
+ auto chroma_width = frame->ChromaWidth();
+ auto chroma_height = frame->ChromaHeight();
+ std::lock_guard<std::mutex> lock(frame_mutex_);
+ return cvd_connection_.Write(size) &&
+ cvd_connection_.WriteStrides(y, frame->width(), frame->height(),
+ frame->StrideY()) &&
+ cvd_connection_.WriteStrides(u, chroma_width, chroma_height,
+ frame->StrideU()) &&
+ cvd_connection_.WriteStrides(v, chroma_width, chroma_height,
+ frame->StrideV());
+}
+
+bool CameraStreamer::IsConnectionReady() {
+ if (!pending_connection_.valid()) {
+ return cvd_connection_.IsConnected();
+ } else if (pending_connection_.wait_for(std::chrono::seconds(0)) !=
+ std::future_status::ready) {
+ // Still waiting for connection
+ return false;
+ } else if (settings_buffer_.empty()) {
+ // connection is ready but we have not yet received client
+ // camera settings
+ return false;
+ }
+ return pending_connection_.get();
+}
+
+void CameraStreamer::StartReadLoop() {
+ if (reader_thread_.joinable()) {
+ reader_thread_.join();
+ }
+ reader_thread_ = std::thread([this] {
+ while (cvd_connection_.IsConnected()) {
+ auto json_value = cvd_connection_.ReadJsonMessage();
+ if (!json_value.empty()) {
+ SendMessage(json_value);
+ }
+ }
+ LOG(INFO) << "Exit reader thread";
+ });
+}
+
+void CameraStreamer::Disconnect() {
+ cvd_connection_.Disconnect();
+ if (reader_thread_.joinable()) {
+ reader_thread_.join();
+ }
+}
+
+} // namespace webrtc_streaming
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/camera_streamer.h b/host/frontend/webrtc/lib/camera_streamer.h
new file mode 100644
index 0000000..ceab2e6
--- /dev/null
+++ b/host/frontend/webrtc/lib/camera_streamer.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <api/video/i420_buffer.h>
+#include <api/video/video_frame.h>
+#include <api/video/video_sink_interface.h>
+#include <json/json.h>
+
+#include "common/libs/utils/vsock_connection.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
+
+#include <atomic>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+namespace cuttlefish {
+namespace webrtc_streaming {
+
+class CameraStreamer : public rtc::VideoSinkInterface<webrtc::VideoFrame>,
+ public CameraController {
+ public:
+ CameraStreamer(unsigned int port, unsigned int cid);
+ ~CameraStreamer();
+
+ CameraStreamer(const CameraStreamer& other) = delete;
+ CameraStreamer& operator=(const CameraStreamer& other) = delete;
+
+ void OnFrame(const webrtc::VideoFrame& frame) override;
+
+ void HandleMessage(const Json::Value& message) override;
+ void HandleMessage(const std::vector<char>& message) override;
+
+ private:
+ using Resolution = struct {
+ int32_t width;
+ int32_t height;
+ };
+ bool ForwardClientMessage(const Json::Value& message);
+ Resolution GetResolutionFromSettings(const Json::Value& settings);
+ bool VsockSendYUVFrame(const webrtc::I420BufferInterface* frame);
+ bool IsConnectionReady();
+ void StartReadLoop();
+ void Disconnect();
+ std::future<bool> pending_connection_;
+ VsockClientConnection cvd_connection_;
+ std::atomic<Resolution> resolution_;
+ std::mutex settings_mutex_;
+ std::string settings_buffer_;
+ std::mutex frame_mutex_;
+ std::mutex onframe_mutex_;
+ rtc::scoped_refptr<webrtc::I420Buffer> scaled_frame_;
+ unsigned int cid_;
+ unsigned int port_;
+ std::thread reader_thread_;
+};
+
+} // namespace webrtc_streaming
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/client_handler.cpp b/host/frontend/webrtc/lib/client_handler.cpp
index 31fd50b..4893f8d 100644
--- a/host/frontend/webrtc/lib/client_handler.cpp
+++ b/host/frontend/webrtc/lib/client_handler.cpp
@@ -39,6 +39,8 @@
static constexpr auto kInputChannelLabel = "input-channel";
static constexpr auto kAdbChannelLabel = "adb-channel";
static constexpr auto kBluetoothChannelLabel = "bluetooth-channel";
+static constexpr auto kCameraDataChannelLabel = "camera-data-channel";
+static constexpr auto kCameraDataEof = "EOF";
class CvdCreateSessionDescriptionObserver
: public webrtc::CreateSessionDescriptionObserver {
@@ -166,6 +168,22 @@
bool channel_open_reported_ = false;
};
+class CameraChannelHandler : public webrtc::DataChannelObserver {
+ public:
+ CameraChannelHandler(
+ rtc::scoped_refptr<webrtc::DataChannelInterface> bluetooth_channel,
+ std::shared_ptr<ConnectionObserver> observer);
+ ~CameraChannelHandler() override;
+
+ void OnStateChange() override;
+ void OnMessage(const webrtc::DataBuffer &msg) override;
+
+ private:
+ rtc::scoped_refptr<webrtc::DataChannelInterface> camera_channel_;
+ std::shared_ptr<ConnectionObserver> observer_;
+ std::vector<char> receive_buffer_;
+};
+
InputChannelHandler::InputChannelHandler(
rtc::scoped_refptr<webrtc::DataChannelInterface> input_channel,
std::shared_ptr<ConnectionObserver> observer)
@@ -375,22 +393,53 @@
observer_->OnBluetoothMessage(msg.data.cdata(), msg.size());
}
+CameraChannelHandler::CameraChannelHandler(
+ rtc::scoped_refptr<webrtc::DataChannelInterface> camera_channel,
+ std::shared_ptr<ConnectionObserver> observer)
+ : camera_channel_(camera_channel), observer_(observer) {
+ camera_channel_->RegisterObserver(this);
+}
+
+CameraChannelHandler::~CameraChannelHandler() {
+ camera_channel_->UnregisterObserver();
+}
+
+void CameraChannelHandler::OnStateChange() {
+ LOG(VERBOSE) << "Camera channel state changed to "
+ << webrtc::DataChannelInterface::DataStateString(
+ camera_channel_->state());
+}
+
+void CameraChannelHandler::OnMessage(const webrtc::DataBuffer &msg) {
+ auto msg_data = msg.data.cdata<char>();
+ if (msg.size() == strlen(kCameraDataEof) &&
+ !strncmp(msg_data, kCameraDataEof, msg.size())) {
+ // Send complete buffer to observer on EOF marker
+ observer_->OnCameraData(receive_buffer_);
+ receive_buffer_.clear();
+ return;
+ }
+ // Otherwise buffer up data
+ receive_buffer_.insert(receive_buffer_.end(), msg_data,
+ msg_data + msg.size());
+}
+
std::shared_ptr<ClientHandler> ClientHandler::Create(
int client_id, std::shared_ptr<ConnectionObserver> observer,
std::function<void(const Json::Value &)> send_to_client_cb,
- std::function<void()> on_connection_closed_cb) {
+ std::function<void(bool)> on_connection_changed_cb) {
return std::shared_ptr<ClientHandler>(new ClientHandler(
- client_id, observer, send_to_client_cb, on_connection_closed_cb));
+ client_id, observer, send_to_client_cb, on_connection_changed_cb));
}
ClientHandler::ClientHandler(
int client_id, std::shared_ptr<ConnectionObserver> observer,
std::function<void(const Json::Value &)> send_to_client_cb,
- std::function<void()> on_connection_closed_cb)
+ std::function<void(bool)> on_connection_changed_cb)
: client_id_(client_id),
observer_(observer),
send_to_client_(send_to_client_cb),
- on_connection_closed_cb_(on_connection_closed_cb) {}
+ on_connection_changed_cb_(on_connection_changed_cb) {}
ClientHandler::~ClientHandler() {
for (auto &data_channel : data_channels_) {
@@ -450,6 +499,17 @@
return true;
}
+webrtc::VideoTrackInterface *ClientHandler::GetCameraStream() const {
+ for (const auto &tranceiver : peer_connection_->GetTransceivers()) {
+ auto track = tranceiver->receiver()->track();
+ if (track &&
+ track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
+ return static_cast<webrtc::VideoTrackInterface *>(track.get());
+ }
+ }
+ return nullptr;
+}
+
void ClientHandler::LogAndReplyError(const std::string &error_msg) const {
LOG(ERROR) << error_msg;
Json::Value reply;
@@ -607,7 +667,7 @@
// will then wait for the callback to return -> deadlock). Destroying the
// peer_connection_ has the same effect. The only alternative is to postpone
// that operation until after the callback returns.
- on_connection_closed_cb_();
+ on_connection_changed_cb_(false);
}
void ClientHandler::OnConnectionChange(
@@ -625,6 +685,7 @@
control_handler_->Send(msg, size, binary);
return true;
});
+ on_connection_changed_cb_(true);
break;
case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected:
LOG(VERBOSE) << "Client " << client_id_ << ": Connection disconnected";
@@ -667,6 +728,9 @@
} else if (label == kBluetoothChannelLabel) {
bluetooth_handler_.reset(
new BluetoothChannelHandler(data_channel, observer_));
+ } else if (label == kCameraDataChannelLabel) {
+ camera_data_handler_.reset(
+ new CameraChannelHandler(data_channel, observer_));
} else {
LOG(VERBOSE) << "Data channel connected: " << label;
data_channels_.push_back(data_channel);
diff --git a/host/frontend/webrtc/lib/client_handler.h b/host/frontend/webrtc/lib/client_handler.h
index c85f169..f7f587b 100644
--- a/host/frontend/webrtc/lib/client_handler.h
+++ b/host/frontend/webrtc/lib/client_handler.h
@@ -21,6 +21,7 @@
#include <optional>
#include <sstream>
#include <string>
+#include <utility>
#include <vector>
#include <json/json.h>
@@ -37,6 +38,7 @@
class AdbChannelHandler;
class ControlChannelHandler;
class BluetoothChannelHandler;
+class CameraChannelHandler;
class ClientHandler : public webrtc::PeerConnectionObserver,
public std::enable_shared_from_this<ClientHandler> {
@@ -44,7 +46,7 @@
static std::shared_ptr<ClientHandler> Create(
int client_id, std::shared_ptr<ConnectionObserver> observer,
std::function<void(const Json::Value&)> send_client_cb,
- std::function<void()> on_connection_closed_cb);
+ std::function<void(bool)> on_connection_changed_cb);
~ClientHandler() override;
bool SetPeerConnection(
@@ -56,6 +58,8 @@
bool AddAudio(rtc::scoped_refptr<webrtc::AudioTrackInterface> track,
const std::string& label);
+ webrtc::VideoTrackInterface* GetCameraStream() const;
+
void HandleMessage(const Json::Value& client_message);
// CreateSessionDescriptionObserver implementation
@@ -107,7 +111,7 @@
};
ClientHandler(int client_id, std::shared_ptr<ConnectionObserver> observer,
std::function<void(const Json::Value&)> send_client_cb,
- std::function<void()> on_connection_closed_cb);
+ std::function<void(bool)> on_connection_changed_cb);
// Intentionally private, disconnect the client by destroying the object.
void Close();
@@ -118,13 +122,14 @@
State state_ = State::kNew;
std::shared_ptr<ConnectionObserver> observer_;
std::function<void(const Json::Value&)> send_to_client_;
- std::function<void()> on_connection_closed_cb_;
+ std::function<void(bool)> on_connection_changed_cb_;
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
std::vector<rtc::scoped_refptr<webrtc::DataChannelInterface>> data_channels_;
std::unique_ptr<InputChannelHandler> input_handler_;
std::unique_ptr<AdbChannelHandler> adb_handler_;
std::unique_ptr<ControlChannelHandler> control_handler_;
std::unique_ptr<BluetoothChannelHandler> bluetooth_handler_;
+ std::unique_ptr<CameraChannelHandler> camera_data_handler_;
};
} // namespace webrtc_streaming
diff --git a/host/frontend/webrtc/lib/connection_observer.h b/host/frontend/webrtc/lib/connection_observer.h
index be6fc13..fe82549 100644
--- a/host/frontend/webrtc/lib/connection_observer.h
+++ b/host/frontend/webrtc/lib/connection_observer.h
@@ -45,6 +45,7 @@
virtual void OnBluetoothChannelOpen(
std::function<bool(const uint8_t*, size_t)> bluetooth_message_sender) = 0;
virtual void OnBluetoothMessage(const uint8_t* msg, size_t size) = 0;
+ virtual void OnCameraData(const std::vector<char>& data) = 0;
};
class ConnectionObserverFactory {
diff --git a/host/frontend/webrtc/lib/streamer.cpp b/host/frontend/webrtc/lib/streamer.cpp
index 7fee1de..71bbab5 100644
--- a/host/frontend/webrtc/lib/streamer.cpp
+++ b/host/frontend/webrtc/lib/streamer.cpp
@@ -34,6 +34,7 @@
#include "host/frontend/webrtc/lib/audio_device.h"
#include "host/frontend/webrtc/lib/audio_track_source_impl.h"
+#include "host/frontend/webrtc/lib/camera_streamer.h"
#include "host/frontend/webrtc/lib/client_handler.h"
#include "host/frontend/webrtc/lib/port_range_socket_factory.h"
#include "host/frontend/webrtc/lib/video_track_source_impl.h"
@@ -141,6 +142,7 @@
void SendMessageToClient(int client_id, const Json::Value& msg);
void DestroyClientHandler(int client_id);
+ void SetupCameraForClient(int client_id);
// WsObserver
void OnOpen() override;
@@ -170,6 +172,7 @@
std::map<std::string, std::string> hardware_;
std::vector<ControlPanelButtonDescriptor> custom_control_panel_buttons_;
std::shared_ptr<AudioDeviceModuleWrapper> audio_device_module_;
+ std::unique_ptr<CameraStreamer> camera_streamer_;
};
Streamer::Streamer(std::unique_ptr<Streamer::Impl> impl)
@@ -263,6 +266,11 @@
return impl_->audio_device_module_;
}
+CameraController* Streamer::AddCamera(unsigned int port, unsigned int cid) {
+ impl_->camera_streamer_ = std::make_unique<CameraStreamer>(port, cid);
+ return impl_->camera_streamer_.get();
+}
+
void Streamer::SetHardwareSpec(std::string key, std::string value) {
impl_->hardware_.emplace(key, value);
}
@@ -566,7 +574,13 @@
[this, client_id](const Json::Value& msg) {
SendMessageToClient(client_id, msg);
},
- [this, client_id] { DestroyClientHandler(client_id); });
+ [this, client_id](bool isOpen) {
+ if (isOpen) {
+ SetupCameraForClient(client_id);
+ } else {
+ DestroyClientHandler(client_id);
+ }
+ });
webrtc::PeerConnectionInterface::RTCConfiguration config;
config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
@@ -641,5 +655,19 @@
});
}
+void Streamer::Impl::SetupCameraForClient(int client_id) {
+ if (!camera_streamer_) {
+ return;
+ }
+ auto client_handler = clients_[client_id];
+ if (client_handler) {
+ auto camera_track = client_handler->GetCameraStream();
+ if (camera_track) {
+ camera_track->AddOrUpdateSink(camera_streamer_.get(),
+ rtc::VideoSinkWants());
+ }
+ }
+}
+
} // namespace webrtc_streaming
} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/streamer.h b/host/frontend/webrtc/lib/streamer.h
index 0628e7d..50a7e12 100644
--- a/host/frontend/webrtc/lib/streamer.h
+++ b/host/frontend/webrtc/lib/streamer.h
@@ -28,6 +28,7 @@
#include "host/frontend/webrtc/lib/audio_sink.h"
#include "host/frontend/webrtc/lib/audio_source.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
#include "host/frontend/webrtc/lib/connection_observer.h"
#include "host/frontend/webrtc/lib/local_recorder.h"
#include "host/frontend/webrtc/lib/video_sink.h"
@@ -98,6 +99,8 @@
// stream here.
std::shared_ptr<AudioSource> GetAudioSource();
+ CameraController* AddCamera(unsigned int port, unsigned int cid);
+
// Add a custom button to the control panel.
void AddCustomControlPanelButton(const std::string& command,
const std::string& title,
diff --git a/host/frontend/webrtc/main.cpp b/host/frontend/webrtc/main.cpp
index 007c405..57d5644 100644
--- a/host/frontend/webrtc/main.cpp
+++ b/host/frontend/webrtc/main.cpp
@@ -33,8 +33,10 @@
#include "host/frontend/webrtc/connection_observer.h"
#include "host/frontend/webrtc/display_handler.h"
#include "host/frontend/webrtc/kernel_log_events_handler.h"
+#include "host/frontend/webrtc/lib/camera_controller.h"
#include "host/frontend/webrtc/lib/local_recorder.h"
#include "host/frontend/webrtc/lib/streamer.h"
+#include "host/frontend/webrtc/lib/video_sink.h"
#include "host/libs/audio_connector/server.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/config/logging.h"
@@ -42,7 +44,8 @@
#include "host/libs/confui/host_server.h"
#include "host/libs/screen_connector/screen_connector.h"
-DEFINE_int32(touch_fd, -1, "An fd to listen on for touch connections.");
+DEFINE_string(touch_fds, "",
+ "A list of fds to listen on for touch connections.");
DEFINE_int32(keyboard_fd, -1, "An fd to listen on for keyboard connections.");
DEFINE_int32(switches_fd, -1, "An fd to listen on for switch connections.");
DEFINE_int32(frame_server_fd, -1, "An fd to listen on for frame updates");
@@ -55,6 +58,7 @@
DEFINE_bool(write_virtio_input, true,
"Whether to send input events in virtio format.");
DEFINE_int32(audio_server_fd, -1, "An fd to listen on for audio frames");
+DEFINE_int32(camera_streamer_fd, -1, "An fd to send client camera frames");
using cuttlefish::AudioHandler;
using cuttlefish::CfConnectionObserverFactory;
@@ -63,6 +67,7 @@
using cuttlefish::webrtc_streaming::LocalRecorder;
using cuttlefish::webrtc_streaming::Streamer;
using cuttlefish::webrtc_streaming::StreamerConfig;
+using cuttlefish::webrtc_streaming::VideoSink;
class CfOperatorObserver
: public cuttlefish::webrtc_streaming::OperatorObserver {
@@ -132,11 +137,16 @@
cuttlefish::InputSockets input_sockets;
- input_sockets.touch_server = cuttlefish::SharedFD::Dup(FLAGS_touch_fd);
+ auto counter = 0;
+ for (const auto& touch_fd_str : android::base::Split(FLAGS_touch_fds, ",")) {
+ auto touch_fd = std::stoi(touch_fd_str);
+ input_sockets.touch_servers["display_" + std::to_string(counter++)] =
+ cuttlefish::SharedFD::Dup(touch_fd);
+ close(touch_fd);
+ }
input_sockets.keyboard_server = cuttlefish::SharedFD::Dup(FLAGS_keyboard_fd);
input_sockets.switches_server = cuttlefish::SharedFD::Dup(FLAGS_switches_fd);
auto control_socket = cuttlefish::SharedFD::Dup(FLAGS_command_fd);
- close(FLAGS_touch_fd);
close(FLAGS_keyboard_fd);
close(FLAGS_switches_fd);
close(FLAGS_command_fd);
@@ -145,19 +155,25 @@
// devices have been initialized. That's OK though, because without those
// devices there is no meaningful interaction the user can have with the
// device.
- input_sockets.touch_client =
- cuttlefish::SharedFD::Accept(*input_sockets.touch_server);
+ for (const auto& touch_entry : input_sockets.touch_servers) {
+ input_sockets.touch_clients[touch_entry.first] =
+ cuttlefish::SharedFD::Accept(*touch_entry.second);
+ }
input_sockets.keyboard_client =
cuttlefish::SharedFD::Accept(*input_sockets.keyboard_server);
input_sockets.switches_client =
cuttlefish::SharedFD::Accept(*input_sockets.switches_server);
- std::thread touch_accepter([&input_sockets]() {
- for (;;) {
- input_sockets.touch_client =
- cuttlefish::SharedFD::Accept(*input_sockets.touch_server);
- }
- });
+ std::vector<std::thread> touch_accepters;
+ for (const auto& touch : input_sockets.touch_servers) {
+ auto label = touch.first;
+ touch_accepters.emplace_back([label, &input_sockets]() {
+ for (;;) {
+ input_sockets.touch_clients[label] =
+ cuttlefish::SharedFD::Accept(*input_sockets.touch_servers[label]);
+ }
+ });
+ }
std::thread keyboard_accepter([&input_sockets]() {
for (;;) {
input_sockets.keyboard_client =
@@ -213,11 +229,27 @@
auto streamer = Streamer::Create(streamer_config, observer_factory);
CHECK(streamer) << "Could not create streamer";
- auto display_0 = streamer->AddDisplay(
- "display_0", screen_connector.ScreenWidth(0),
- screen_connector.ScreenHeight(0), cvd_config->dpi(), true);
- auto display_handler = std::shared_ptr<DisplayHandler>(
- new DisplayHandler(display_0, screen_connector));
+ uint32_t display_index = 0;
+ std::vector<std::shared_ptr<VideoSink>> displays;
+ for (const auto& display_config : cvd_config->display_configs()) {
+ const std::string display_id = "display_" + std::to_string(display_index);
+
+ auto display =
+ streamer->AddDisplay(display_id, display_config.width,
+ display_config.height, display_config.dpi, true);
+ displays.push_back(display);
+
+ ++display_index;
+ }
+
+ auto display_handler =
+ std::make_shared<DisplayHandler>(std::move(displays), screen_connector);
+
+ if (instance.camera_server_port()) {
+ auto camera_controller = streamer->AddCamera(instance.camera_server_port(),
+ instance.vsock_guest_cid());
+ observer_factory->SetCameraHandler(camera_controller);
+ }
std::unique_ptr<cuttlefish::webrtc_streaming::LocalRecorder> local_recorder;
if (cvd_config->record_screen()) {
diff --git a/host/frontend/webrtc_operator/assets/index.html b/host/frontend/webrtc_operator/assets/index.html
index 1eae893..f0dc25e 100644
--- a/host/frontend/webrtc_operator/assets/index.html
+++ b/host/frontend/webrtc_operator/assets/index.html
@@ -22,6 +22,7 @@
<link rel="stylesheet" type="text/css" href="controls.css" >
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
@@ -34,13 +35,14 @@
<div id='app-controls'>
<div id="keyboard-capture-control" title="Capture Keyboard"></div>
<div id="mic-capture-control" title="Capture Microphone"></div>
+ <div id="camera-control" title="Capture Camera"></div>
<audio autoplay controls id="device-audio"></audio>
</div>
<div id='status-div'>
<h3 id='status-message' class='connecting'>Connecting to device</h3>
</div>
</div>
- <div id='controls-and-screens'>
+ <div id='controls-and-displays'>
<div id='control-panel-default-buttons' class='control-panel-column'>
<button id='device-details-button' title='Device Details' class='material-icons'>
settings
@@ -50,8 +52,7 @@
</button>
</div>
<div id='control-panel-custom-buttons' class='control-panel-column'></div>
- <div id='screens'>
- <video id="device-screen" autoplay ></video>
+ <div id='device-displays'>
</div>
</div>
</section>
diff --git a/host/frontend/webrtc_operator/assets/js/app.js b/host/frontend/webrtc_operator/assets/js/app.js
index 441df00..db9c8a7 100644
--- a/host/frontend/webrtc_operator/assets/js/app.js
+++ b/host/frontend/webrtc_operator/assets/js/app.js
@@ -22,9 +22,11 @@
createToggleControl(keyboardCaptureCtrl, "keyboard", onKeyboardCaptureToggle);
const micCaptureCtrl = document.getElementById('mic-capture-control');
createToggleControl(micCaptureCtrl, "mic", onMicCaptureToggle);
+ const cameraCtrl = document.getElementById('camera-control');
+ createToggleControl(cameraCtrl, "videocam", onVideoCaptureToggle);
- const deviceScreen = document.getElementById('device-screen');
const deviceAudio = document.getElementById('device-audio');
+ const deviceDisplays = document.getElementById('device-displays');
const statusMessage = document.getElementById('status-message');
let connectionAttemptDuration = 0;
@@ -47,23 +49,6 @@
}
}, intervalMs);
- deviceScreen.addEventListener('loadeddata', (evt) => {
- clearInterval(animateDeviceStatusMessage);
- statusMessage.textContent = 'Awaiting adb connection...';
- resizeDeviceView();
- deviceScreen.style.visibility = 'visible';
- // Enable the buttons after the screen is visible.
- for (const [_, button] of Object.entries(buttons)) {
- if (!button.adb) {
- button.button.disabled = false;
- }
- }
- // Start the adb connection if it is not already started.
- initializeAdb();
- });
-
- let videoStream;
- let display_label;
let buttons = {};
let mouseIsDown = false;
let deviceConnection;
@@ -106,7 +91,7 @@
}
let currentRotation = 0;
- let currentDisplayDetails;
+ let currentDisplayDescriptions;
function onControlMessage(message) {
let message_data = JSON.parse(message.data);
console.log(message_data)
@@ -120,46 +105,234 @@
if (message_data.event == 'VIRTUAL_DEVICE_SCREEN_CHANGED') {
if (metadata.rotation != currentRotation) {
// Animate the screen rotation.
- deviceScreen.style.transition = 'transform 1s';
- } else {
- // Don't animate screen resizes, since these appear as odd sliding
- // animations if the screen is rotated due to the translateY.
- deviceScreen.style.transition = '';
+ const targetRotation = metadata.rotation == 0 ? 0 : -90;
+
+ $(deviceDisplays).animate(
+ {
+ textIndent: targetRotation,
+ },
+ {
+ duration: 1000,
+ step: function(now, tween) {
+ resizeDeviceDisplays();
+ },
+ }
+ );
}
currentRotation = metadata.rotation;
- updateDeviceDisplayDetails({
- dpi: metadata.dpi,
- x_res: metadata.width,
- y_res: metadata.height
+ }
+ if (message_data.event == 'VIRTUAL_DEVICE_CAPTURE_IMAGE') {
+ if (deviceConnection.cameraEnabled) {
+ takePhoto();
+ }
+ }
+ if (message_data.event == 'VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED') {
+ updateDisplayVisibility(metadata.display, metadata.mode);
+ }
+ }
+
+ function updateDisplayVisibility(displayId, powerMode) {
+ const display = document.getElementById('display_' + displayId).parentElement;
+ if (display == null) {
+ console.error('Unknown display id: ' + displayId);
+ return;
+ }
+ switch (powerMode) {
+ case 'On':
+ display.style.visibility = 'visible';
+ break;
+ case 'Off':
+ display.style.visibility = 'hidden';
+ break;
+ default:
+ console.error('Display ' + displayId + ' has unknown display power mode: ' + powerMode);
+ }
+ }
+
+ function getTransformRotation(element) {
+ if (!element.style.textIndent) {
+ return 0;
+ }
+ // Remove 'px' and convert to float.
+ return parseFloat(element.style.textIndent.slice(0, -2));
+ }
+
+ let anyDeviceDisplayLoaded = false;
+ function onDeviceDisplayLoaded() {
+ if (anyDeviceDisplayLoaded) {
+ return;
+ }
+ anyDeviceDisplayLoaded = true;
+
+ clearInterval(animateDeviceStatusMessage);
+ statusMessage.textContent = 'Awaiting bootup and adb connection. Please wait...';
+ resizeDeviceDisplays();
+
+ let deviceDisplayList =
+ document.getElementsByClassName("device-display");
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.style.visibility = 'visible';
+ }
+
+ // Enable the buttons after the screen is visible.
+ for (const [_, button] of Object.entries(buttons)) {
+ if (!button.adb) {
+ button.button.disabled = false;
+ }
+ }
+ // Start the adb connection if it is not already started.
+ initializeAdb();
+ }
+
+ // Creates a <video> element and a <div> container element for each display.
+ // The extra <div> container elements are used to maintain the width and
+ // height of the device as the CSS 'transform' property used on the <video>
+ // element for rotating the device only affects the visuals of the element
+ // and not its layout.
+ function createDeviceDisplays(devConn) {
+ for (const deviceDisplayDescription of currentDisplayDescriptions) {
+ let deviceDisplay = document.createElement("div");
+ deviceDisplay.classList.add("device-display");
+ // Start the screen as hidden. Only show when data is ready.
+ deviceDisplay.style.visibility = 'hidden';
+
+ let deviceDisplayInfo = document.createElement("div");
+ deviceDisplayInfo.classList.add("device-display-info");
+ deviceDisplayInfo.id = deviceDisplayDescription.stream_id + '_info';
+ deviceDisplay.appendChild(deviceDisplayInfo);
+
+ let deviceDisplayVideo = document.createElement("video");
+ deviceDisplayVideo.autoplay = true;
+ deviceDisplayVideo.id = deviceDisplayDescription.stream_id;
+ deviceDisplayVideo.classList.add("device-display-video");
+ deviceDisplayVideo.addEventListener('loadeddata', (evt) => {
+ onDeviceDisplayLoaded();
});
+ deviceDisplay.appendChild(deviceDisplayVideo);
- resizeDeviceView();
+ deviceDisplays.appendChild(deviceDisplay);
+
+ let stream_id = deviceDisplayDescription.stream_id;
+ devConn.getStream(stream_id).then(stream => {
+ deviceDisplayVideo.srcObject = stream;
+ }).catch(e => console.error('Unable to get display stream: ', e));
}
}
- const screensDiv = document.getElementById('screens');
- function resizeDeviceView() {
+ function takePhoto() {
+ const imageCapture = deviceConnection.imageCapture;
+ if (imageCapture) {
+ const photoSettings = {
+ imageWidth: deviceConnection.cameraWidth,
+ imageHeight: deviceConnection.cameraHeight
+ }
+ imageCapture.takePhoto(photoSettings)
+ .then(blob => blob.arrayBuffer())
+ .then(buffer => deviceConnection.sendOrQueueCameraData(buffer))
+ .catch(error => console.log(error));
+ }
+ }
+
+ function resizeDeviceDisplays() {
+ // Padding between displays.
+ const deviceDisplayWidthPadding = 10;
+ // Padding for the display info above each display video.
+ const deviceDisplayHeightPadding = 38;
+
+ let deviceDisplayList =
+ document.getElementsByClassName("device-display");
+ let deviceDisplayVideoList =
+ document.getElementsByClassName("device-display-video");
+ let deviceDisplayInfoList =
+ document.getElementsByClassName("device-display-info");
+
+ const rotationDegrees = getTransformRotation(deviceDisplays);
+ const rotationRadians = rotationDegrees * Math.PI / 180;
+
// Auto-scale the screen based on window size.
- // Max window width of 70%, allowing space for the control panel.
- let ww = screensDiv.offsetWidth * 0.7;
- let wh = screensDiv.offsetHeight;
- let vw = currentDisplayDetails.x_res;
- let vh = currentDisplayDetails.y_res;
- let scaling = vw * wh > vh * ww ? ww / vw : wh / vh;
- if (currentRotation == 0) {
- deviceScreen.style.transform = null;
- deviceScreen.style.width = vw * scaling;
- deviceScreen.style.height = vh * scaling;
- } else if (currentRotation == 1) {
- deviceScreen.style.transform =
- `rotateZ(-90deg) translateY(-${vh * scaling}px)`;
- // When rotated, w and h are swapped.
- deviceScreen.style.width = vh * scaling;
- deviceScreen.style.height = vw * scaling;
+ let availableWidth = deviceDisplays.clientWidth;
+ let availableHeight = deviceDisplays.clientHeight - deviceDisplayHeightPadding;
+
+ // Reserve space for padding between the displays.
+ availableWidth = availableWidth -
+ (currentDisplayDescriptions.length * deviceDisplayWidthPadding);
+
+ // Loop once over all of the displays to compute the total space needed.
+ let neededWidth = 0;
+ let neededHeight = 0;
+ for (let i = 0; i < deviceDisplayList.length; i++) {
+ let deviceDisplayDescription = currentDisplayDescriptions[i];
+ let deviceDisplayVideo = deviceDisplayVideoList[i];
+
+ const originalDisplayWidth = deviceDisplayDescription.x_res;
+ const originalDisplayHeight = deviceDisplayDescription.y_res;
+
+ const neededBoundingBoxWidth =
+ Math.abs(Math.cos(rotationRadians) * originalDisplayWidth) +
+ Math.abs(Math.sin(rotationRadians) * originalDisplayHeight);
+ const neededBoundingBoxHeight =
+ Math.abs(Math.sin(rotationRadians) * originalDisplayWidth) +
+ Math.abs(Math.cos(rotationRadians) * originalDisplayHeight);
+
+ neededWidth = neededWidth + neededBoundingBoxWidth;
+ neededHeight = Math.max(neededHeight, neededBoundingBoxHeight);
+ }
+
+ const scaling = Math.min(availableWidth / neededWidth,
+ availableHeight / neededHeight);
+
+ // Loop again over all of the displays to set the sizes and positions.
+ let deviceDisplayLeftOffset = 0;
+ for (let i = 0; i < deviceDisplayList.length; i++) {
+ let deviceDisplay = deviceDisplayList[i];
+ let deviceDisplayVideo = deviceDisplayVideoList[i];
+ let deviceDisplayInfo = deviceDisplayInfoList[i];
+ let deviceDisplayDescription = currentDisplayDescriptions[i];
+
+ let rotated = currentRotation == 1 ? ' (Rotated)' : '';
+ deviceDisplayInfo.textContent = `Display ${i} - ` +
+ `${deviceDisplayDescription.x_res}x` +
+ `${deviceDisplayDescription.y_res} ` +
+ `(${deviceDisplayDescription.dpi} DPI)${rotated}`;
+
+ const originalDisplayWidth = deviceDisplayDescription.x_res;
+ const originalDisplayHeight = deviceDisplayDescription.y_res;
+
+ const scaledDisplayWidth = originalDisplayWidth * scaling;
+ const scaledDisplayHeight = originalDisplayHeight * scaling;
+
+ const neededBoundingBoxWidth =
+ Math.abs(Math.cos(rotationRadians) * originalDisplayWidth) +
+ Math.abs(Math.sin(rotationRadians) * originalDisplayHeight);
+ const neededBoundingBoxHeight =
+ Math.abs(Math.sin(rotationRadians) * originalDisplayWidth) +
+ Math.abs(Math.cos(rotationRadians) * originalDisplayHeight);
+
+ const scaledBoundingBoxWidth = neededBoundingBoxWidth * scaling;
+ const scaledBoundingBoxHeight = neededBoundingBoxHeight * scaling;
+
+ const offsetX = (scaledBoundingBoxWidth - scaledDisplayWidth) / 2;
+ const offsetY = (scaledBoundingBoxHeight - scaledDisplayHeight) / 2;
+
+ deviceDisplayVideo.style.width = scaledDisplayWidth;
+ deviceDisplayVideo.style.height = scaledDisplayHeight;
+ deviceDisplayVideo.style.transform =
+ `translateX(${offsetX}px) ` +
+ `translateY(${offsetY}px) ` +
+ `rotateZ(${rotationDegrees}deg) `;
+
+ deviceDisplay.style.left = `${deviceDisplayLeftOffset}px`;
+ deviceDisplay.style.width = scaledBoundingBoxWidth;
+ deviceDisplay.style.height = scaledBoundingBoxHeight;
+
+ deviceDisplayLeftOffset =
+ deviceDisplayLeftOffset +
+ deviceDisplayWidthPadding +
+ scaledBoundingBoxWidth;
}
}
- window.onresize = resizeDeviceView;
+ window.onresize = resizeDeviceDisplays;
function createControlPanelButton(command, title, icon_name,
listener=onControlPanelButton,
@@ -268,7 +441,7 @@
statusMessage.textContent = 'No connection to the guest device. ' +
'Please ensure the WebRTC process on the host machine is active.';
statusMessage.style.visibility = 'visible';
- deviceScreen.style.display = 'none';
+ deviceDisplays.style.display = 'none';
for (const [_, button] of Object.entries(buttons)) {
button.button.disabled = true;
}
@@ -278,15 +451,12 @@
.then(webrtcModule => webrtcModule.Connect(device_id, options))
.then(devConn => {
deviceConnection = devConn;
- // TODO(b/143667633): get multiple display configuration from the
- // description object
+
console.log(deviceConnection.description);
- let stream_id = devConn.description.displays[0].stream_id;
- devConn.getStream(stream_id).then(stream => {
- videoStream = stream;
- display_label = stream_id;
- deviceScreen.srcObject = videoStream;
- }).catch(e => console.error('Unable to get display stream: ', e));
+
+ currentDisplayDescriptions = devConn.description.displays;
+
+ createDeviceDisplays(devConn);
for (const audio_desc of devConn.description.audio_streams) {
let stream_id = audio_desc.stream_id;
devConn.getStream(stream_id).then(stream => {
@@ -295,7 +465,6 @@
}
startMouseTracking(); // TODO stopMouseTracking() when disconnected
updateDeviceHardwareDetails(deviceConnection.description.hardware);
- updateDeviceDisplayDetails(deviceConnection.description.displays[0]);
if (deviceConnection.description.custom_control_panel_buttons.length > 0) {
document.getElementById('control-panel-custom-buttons').style.display = 'flex';
for (const button of deviceConnection.description.custom_control_panel_buttons) {
@@ -326,8 +495,6 @@
}
}
deviceConnection.onControlMessage(msg => onControlMessage(msg));
- // Start the screen as hidden. Only show when data is ready.
- deviceScreen.style.visibility = 'hidden';
// Show the error message and disable buttons when the WebRTC connection fails.
deviceConnection.onConnectionStateChange(state => {
if (state == 'disconnected' || state == 'failed') {
@@ -343,13 +510,11 @@
});
let hardwareDetailsText = '';
- let displayDetailsText = '';
let deviceStateDetailsText = '';
function updateDeviceDetailsText() {
document.getElementById('device-details-hardware').textContent = [
hardwareDetailsText,
deviceStateDetailsText,
- displayDetailsText,
].filter(e => e /*remove empty*/).join('\n');
}
function updateDeviceHardwareDetails(hardware) {
@@ -362,15 +527,6 @@
hardwareDetailsText = hardwareDetailsTextLines.join('\n');
updateDeviceDetailsText();
}
- function updateDeviceDisplayDetails(display) {
- currentDisplayDetails = display;
- let dpi = display.dpi;
- let x_res = display.x_res;
- let y_res = display.y_res;
- let rotated = currentRotation == 1 ? ' (Rotated)' : '';
- displayDetailsText = `Display - ${x_res}x${y_res} (${dpi}DPI)${rotated}`;
- updateDeviceDetailsText();
- }
function updateDeviceStateDetails() {
let deviceStateDetailsTextLines = [];
if (deviceStateLidSwitchOpen != null) {
@@ -396,6 +552,10 @@
deviceConnection.useMic(enabled);
}
+ function onVideoCaptureToggle(enabled) {
+ deviceConnection.useVideo(enabled);
+ }
+
function cmdConsole(consoleViewName, consoleInputName) {
let consoleView = document.getElementById(consoleViewName);
@@ -523,34 +683,48 @@
}
function startMouseTracking() {
+ let deviceDisplayList = document.getElementsByClassName("device-display");
if (window.PointerEvent) {
- deviceScreen.addEventListener('pointerdown', onStartDrag);
- deviceScreen.addEventListener('pointermove', onContinueDrag);
- deviceScreen.addEventListener('pointerup', onEndDrag);
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.addEventListener('pointerdown', onStartDrag);
+ deviceDisplay.addEventListener('pointermove', onContinueDrag);
+ deviceDisplay.addEventListener('pointerup', onEndDrag);
+ }
} else if (window.TouchEvent) {
- deviceScreen.addEventListener('touchstart', onStartDrag);
- deviceScreen.addEventListener('touchmove', onContinueDrag);
- deviceScreen.addEventListener('touchend', onEndDrag);
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.addEventListener('touchstart', onStartDrag);
+ deviceDisplay.addEventListener('touchmove', onContinueDrag);
+ deviceDisplay.addEventListener('touchend', onEndDrag);
+ }
} else if (window.MouseEvent) {
- deviceScreen.addEventListener('mousedown', onStartDrag);
- deviceScreen.addEventListener('mousemove', onContinueDrag);
- deviceScreen.addEventListener('mouseup', onEndDrag);
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.addEventListener('mousedown', onStartDrag);
+ deviceDisplay.addEventListener('mousemove', onContinueDrag);
+ deviceDisplay.addEventListener('mouseup', onEndDrag);
+ }
}
}
function stopMouseTracking() {
+ let deviceDisplayList = document.getElementsByClassName("device-display");
if (window.PointerEvent) {
- deviceScreen.removeEventListener('pointerdown', onStartDrag);
- deviceScreen.removeEventListener('pointermove', onContinueDrag);
- deviceScreen.removeEventListener('pointerup', onEndDrag);
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.removeEventListener('pointerdown', onStartDrag);
+ deviceDisplay.removeEventListener('pointermove', onContinueDrag);
+ deviceDisplay.removeEventListener('pointerup', onEndDrag);
+ }
} else if (window.TouchEvent) {
- deviceScreen.removeEventListener('touchstart', onStartDrag);
- deviceScreen.removeEventListener('touchmove', onContinueDrag);
- deviceScreen.removeEventListener('touchend', onEndDrag);
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.removeEventListener('touchstart', onStartDrag);
+ deviceDisplay.removeEventListener('touchmove', onContinueDrag);
+ deviceDisplay.removeEventListener('touchend', onEndDrag);
+ }
} else if (window.MouseEvent) {
- deviceScreen.removeEventListener('mousedown', onStartDrag);
- deviceScreen.removeEventListener('mousemove', onContinueDrag);
- deviceScreen.removeEventListener('mouseup', onEndDrag);
+ for (const deviceDisplay of deviceDisplayList) {
+ deviceDisplay.removeEventListener('mousedown', onStartDrag);
+ deviceDisplay.removeEventListener('mousemove', onContinueDrag);
+ deviceDisplay.removeEventListener('mouseup', onEndDrag);
+ }
}
}
@@ -596,14 +770,17 @@
console.assert(deviceConnection, 'Can\'t send mouse update without device');
var eventType = e.type.substring(0, 5);
+ // The <video> element:
+ const deviceDisplay = e.target;
+
// Before the first video frame arrives there is no way to know width and
// height of the device's screen, so turn every click into a click at 0x0.
// A click at that position is not more dangerous than anywhere else since
// the user is clicking blind anyways.
- const videoWidth = deviceScreen.videoWidth? deviceScreen.videoWidth: 1;
- const videoHeight = deviceScreen.videoHeight? deviceScreen.videoHeight: 1;
- const elementWidth = deviceScreen.offsetWidth? deviceScreen.offsetWidth: 1;
- const elementHeight = deviceScreen.offsetHeight? deviceScreen.offsetHeight: 1;
+ const videoWidth = deviceDisplay.videoWidth? deviceDisplay.videoWidth: 1;
+ const videoHeight = deviceDisplay.videoHeight? deviceDisplay.videoHeight: 1;
+ const elementWidth = deviceDisplay.offsetWidth? deviceDisplay.offsetWidth: 1;
+ const elementHeight = deviceDisplay.offsetHeight? deviceDisplay.offsetHeight: 1;
// vh*ew > eh*vw? then scale h instead of w
const scaleHeight = videoHeight * elementWidth > videoWidth * elementHeight;
@@ -732,6 +909,8 @@
// NOTE: Rotation is handled automatically because the CSS rotation through
// transforms also rotates the coordinates of events on the object.
+ const display_label = deviceDisplay.id;
+
deviceConnection.sendMultiTouch(
{idArr, xArr, yArr, down, slotArr, display_label});
}
diff --git a/host/frontend/webrtc_operator/assets/js/cf_webrtc.js b/host/frontend/webrtc_operator/assets/js/cf_webrtc.js
index c9d8213..c83ee38 100644
--- a/host/frontend/webrtc_operator/assets/js/cf_webrtc.js
+++ b/host/frontend/webrtc_operator/assets/js/cf_webrtc.js
@@ -82,12 +82,22 @@
}
class DeviceConnection {
- constructor(pc, control, audio_stream) {
+ constructor(pc, control, media_stream) {
this._pc = pc;
this._control = control;
- this._audio_stream = audio_stream;
+ this._media_stream = media_stream;
// Disable the microphone by default
this.useMic(false);
+ this.useVideo(false);
+ this._cameraDataChannel = pc.createDataChannel('camera-data-channel');
+ this._cameraDataChannel.binaryType = 'arraybuffer'
+ this._cameraInputQueue = new Array();
+ var self = this;
+ this._cameraDataChannel.onbufferedamountlow = () => {
+ if (self._cameraInputQueue.length > 0) {
+ self.sendCameraData(self._cameraInputQueue.shift());
+ }
+ }
this._inputChannel = createDataChannel(pc, 'input-channel');
this._adbChannel = createDataChannel(pc, 'adb-channel', (msg) => {
if (this._onAdbMessage) {
@@ -110,6 +120,7 @@
console.error('Received unexpected Bluetooth message');
}
});
+ this.sendCameraResolution();
this._streams = {};
this._streamPromiseResolvers = {};
@@ -135,6 +146,28 @@
return this._description;
}
+ get imageCapture() {
+ if (this._media_stream) {
+ const track = this._media_stream.getVideoTracks()[0]
+ return new ImageCapture(track);
+ }
+ return undefined;
+ }
+
+ get cameraWidth() {
+ return this._x_res;
+ }
+
+ get cameraHeight() {
+ return this._y_res;
+ }
+
+ get cameraEnabled() {
+ if (this._media_stream) {
+ return this._media_stream.getVideoTracks().some(track => track.enabled);
+ }
+ }
+
getStream(stream_id) {
return new Promise((resolve, reject) => {
if (this._streams[stream_id]) {
@@ -200,11 +233,53 @@
}
useMic(in_use) {
- if (this._audio_stream) {
- this._audio_stream.getTracks().forEach(track => track.enabled = in_use);
+ if (this._media_stream) {
+ this._media_stream.getAudioTracks().forEach(track => track.enabled = in_use);
}
}
+ useVideo(in_use) {
+ if (this._media_stream) {
+ this._media_stream.getVideoTracks().forEach(track => track.enabled = in_use);
+ }
+ }
+
+ sendCameraResolution() {
+ if (this._media_stream) {
+ const cameraTracks = this._media_stream.getVideoTracks();
+ if (cameraTracks.length > 0) {
+ const settings = cameraTracks[0].getSettings();
+ this._x_res = settings.width;
+ this._y_res = settings.height;
+ this.sendControlMessage(JSON.stringify({
+ command: 'camera_settings',
+ width: settings.width,
+ height: settings.height,
+ frame_rate: settings.frameRate,
+ facing: settings.facingMode
+ }));
+ }
+ }
+ }
+
+ sendOrQueueCameraData(data) {
+ if (this._cameraDataChannel.bufferedAmount > 0 || this._cameraInputQueue.length > 0) {
+ this._cameraInputQueue.push(data);
+ } else {
+ this.sendCameraData(data);
+ }
+ }
+
+ sendCameraData(data) {
+ const MAX_SIZE = 65535;
+ const END_MARKER = 'EOF';
+ for (let i = 0; i < data.byteLength; i += MAX_SIZE) {
+ // range is clamped to the valid index range
+ this._cameraDataChannel.send(data.slice(i, i + MAX_SIZE));
+ }
+ this._cameraDataChannel.send(END_MARKER);
+ }
+
// Provide a callback to receive control-related comms from the device
onControlMessage(cb) {
this._onControlMessage = cb;
@@ -404,21 +479,20 @@
}
let pc = createPeerConnection(infraConfig, control);
- let audioStream;
+ let mediaStream;
try {
- audioStream =
- await navigator.mediaDevices.getUserMedia({video: false, audio: true});
- const audioTracks = audioStream.getAudioTracks();
- if (audioTracks.length > 0) {
- console.log(`Using Audio device: ${audioTracks[0].label}, with ${
- audioTracks.length} tracks`);
- audioTracks.forEach(track => pc.addTrack(track, audioStream));
- }
+ mediaStream =
+ await navigator.mediaDevices.getUserMedia({video: true, audio: true});
+ const tracks = mediaStream.getTracks();
+ tracks.forEach(track => {
+ console.log(`Using ${track.kind} device: ${track.label}`);
+ pc.addTrack(track, mediaStream);
+ });
} catch (e) {
console.error("Failed to open audio device: ", e);
}
- let deviceConnection = new DeviceConnection(pc, control, audioStream);
+ let deviceConnection = new DeviceConnection(pc, control, mediaStream);
deviceConnection.description = deviceInfo;
async function acceptOfferAndReplyAnswer(offer) {
try {
diff --git a/host/frontend/webrtc_operator/assets/style.css b/host/frontend/webrtc_operator/assets/style.css
index 5d59b34..a594e99 100644
--- a/host/frontend/webrtc_operator/assets/style.css
+++ b/host/frontend/webrtc_operator/assets/style.css
@@ -17,6 +17,8 @@
body {
background-color:black;
margin: 0;
+ touch-action: none;
+ overscroll-behavior: none;
}
#device-selector {
@@ -85,13 +87,14 @@
/* Control panel buttons and device screen(s). */
-#controls-and-screens {
+#controls-and-displays {
height: calc(100% - 84px);
/* Items inside this use a row Flexbox.*/
display: flex;
}
-#controls-and-screens div {
+
+#controls-and-displays > div {
margin-left: 5px;
margin-right: 5px;
}
@@ -163,15 +166,41 @@
background-color: #5f6368; /* Google grey 700 */
}
-#screens {
+#device-displays {
/* Take up the remaining width of the window.*/
flex-grow: 1;
/* Don't grow taller than the window.*/
max-height: 100vh;
+ /* Allows child elements to be positioned relative to this element. */
+ position: relative;
}
-#device-screen {
+/*
+ * Container <div> used to wrap each display's <video> element which is used for
+ * maintaining each display's width and height while the display is potentially
+ * rotating.
+ */
+.device-display {
+ /* Prevents #device-displays from using this element when computing flex size. */
+ position: absolute;
+}
+
+/* Container <div> to show info about the individual display. */
+.device-display-info {
+ color: white;
+ /* dark green */
+ background-color: #007000;
+ font-family: 'Open Sans', sans-serif;
+ text-indent: 0px;
+ border-radius: 10px;
+ padding: 10px;
+ margin-bottom: 10px;
+}
+
+/* The actual <video> element for each display. */
+.device-display-video {
+ position: absolute;
+ left: 0px;
touch-action: none;
- transform-origin: top right;
object-fit: cover;
}
diff --git a/host/frontend/webrtc_operator/server.cpp b/host/frontend/webrtc_operator/server.cpp
index 565676a..c1f08f7 100644
--- a/host/frontend/webrtc_operator/server.cpp
+++ b/host/frontend/webrtc_operator/server.cpp
@@ -31,7 +31,9 @@
DEFINE_bool(use_secure_http, true, "Whether to use HTTPS or HTTP.");
DEFINE_string(assets_dir, "webrtc",
"Directory with location of webpage assets.");
-DEFINE_string(certs_dir, "webrtc/certs", "Directory to certificates.");
+DEFINE_string(certs_dir, "webrtc/certs",
+ "Directory to certificates. It must contain a server.crt file, a "
+ "server.key file and (optionally) a CA.crt file.");
DEFINE_string(stun_server, "stun.l.google.com:19302",
"host:port of STUN server to use for public address resolution");
diff --git a/host/libs/audio_connector/commands.cpp b/host/libs/audio_connector/commands.cpp
index 42536d5..b77c726 100644
--- a/host/libs/audio_connector/commands.cpp
+++ b/host/libs/audio_connector/commands.cpp
@@ -15,6 +15,8 @@
#include "host/libs/audio_connector/commands.h"
+#include <algorithm>
+
#include <android-base/logging.h>
#include "host/libs/audio_connector/shm_layout.h"
@@ -27,6 +29,50 @@
<< " went out of scope without reply";
}
+JackInfoCommand::JackInfoCommand(uint32_t start_id, size_t count,
+ virtio_snd_jack_info* jack_info)
+ : InfoCommand(AudioCommandType::VIRTIO_SND_R_CHMAP_INFO, start_id, count,
+ jack_info) {}
+
+void JackInfoCommand::Reply(AudioStatus status,
+ const std::vector<virtio_snd_jack_info>& reply) {
+ MarkReplied(status);
+ if (status != AudioStatus::VIRTIO_SND_S_OK) {
+ return;
+ }
+ CHECK(reply.size() == count())
+ << "Returned unmatching info count: " << reply.size() << " vs "
+ << count();
+ for (int i = 0; i < reply.size(); ++i) {
+ info_reply()[i] = reply[i];
+ }
+}
+
+ChmapInfoCommand::ChmapInfoCommand(uint32_t start_id, size_t count,
+ virtio_snd_chmap_info* chmap_info)
+ : InfoCommand(AudioCommandType::VIRTIO_SND_R_CHMAP_INFO, start_id, count,
+ chmap_info) {}
+
+void ChmapInfoCommand::Reply(AudioStatus status,
+ const std::vector<virtio_snd_chmap_info>& reply) {
+ MarkReplied(status);
+ if (status != AudioStatus::VIRTIO_SND_S_OK) {
+ return;
+ }
+ CHECK(reply.size() == count())
+ << "Returned unmatching info count: " << reply.size() << " vs "
+ << count();
+ for (int i = 0; i < reply.size(); ++i) {
+ info_reply()[i].hdr.hda_fn_nid = Le32(reply[i].hdr.hda_fn_nid);
+ info_reply()[i].direction = reply[i].direction;
+ auto channels = std::min(VIRTIO_SND_CHMAP_MAX_SIZE, reply[i].channels);
+ info_reply()[i].channels = channels;
+ for (int j = 0; j < channels; ++j) {
+ info_reply()[i].positions[j] = reply[i].positions[j];
+ }
+ }
+}
+
StreamInfoCommand::StreamInfoCommand(uint32_t start_id, size_t count,
virtio_snd_pcm_info* pcm_info)
: InfoCommand(AudioCommandType::VIRTIO_SND_R_PCM_INFO, start_id, count,
diff --git a/host/libs/audio_connector/commands.h b/host/libs/audio_connector/commands.h
index 849dc12..1ae2e6b 100644
--- a/host/libs/audio_connector/commands.h
+++ b/host/libs/audio_connector/commands.h
@@ -60,6 +60,24 @@
R* info_reply_;
};
+class ChmapInfoCommand : public InfoCommand<virtio_snd_chmap_info> {
+ public:
+ ChmapInfoCommand(uint32_t start_id, size_t count,
+ virtio_snd_chmap_info* chmap_info);
+
+ void Reply(AudioStatus status,
+ const std::vector<virtio_snd_chmap_info>& reply);
+};
+
+class JackInfoCommand : public InfoCommand<virtio_snd_jack_info> {
+ public:
+ JackInfoCommand(uint32_t start_id, size_t count,
+ virtio_snd_jack_info* jack_info);
+
+ void Reply(AudioStatus status,
+ const std::vector<virtio_snd_jack_info>& reply);
+};
+
class StreamInfoCommand : public InfoCommand<virtio_snd_pcm_info> {
public:
StreamInfoCommand(uint32_t start_id, size_t count,
diff --git a/host/libs/audio_connector/server.cpp b/host/libs/audio_connector/server.cpp
index b925cdd..c153df9 100644
--- a/host/libs/audio_connector/server.cpp
+++ b/host/libs/audio_connector/server.cpp
@@ -21,6 +21,7 @@
#include <unistd.h>
#include <utility>
+#include <vector>
#include <android-base/logging.h>
@@ -244,8 +245,38 @@
executor.StopStream(cmd);
return CmdReply(cmd.status());
}
- case AudioCommandType::VIRTIO_SND_R_CHMAP_INFO:
- case AudioCommandType::VIRTIO_SND_R_JACK_INFO:
+ case AudioCommandType::VIRTIO_SND_R_CHMAP_INFO: {
+ if (recv_size < sizeof(virtio_snd_query_info)) {
+ LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
+ return false;
+ }
+ auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
+ auto info_count = query_info->count.as_uint32_t();
+ auto start_id = query_info->start_id.as_uint32_t();
+ std::unique_ptr<virtio_snd_chmap_info[]> reply(
+ new virtio_snd_chmap_info[info_count]);
+ ChmapInfoCommand cmd(start_id, info_count, reply.get());
+
+ executor.ChmapsInfo(cmd);
+ return CmdReply(cmd.status(), reply.get(),
+ info_count * sizeof(reply[0]));
+ }
+ case AudioCommandType::VIRTIO_SND_R_JACK_INFO: {
+ if (recv_size < sizeof(virtio_snd_query_info)) {
+ LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
+ return false;
+ }
+ auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
+ auto info_count = query_info->count.as_uint32_t();
+ auto start_id = query_info->start_id.as_uint32_t();
+ std::unique_ptr<virtio_snd_jack_info[]> reply(
+ new virtio_snd_jack_info[info_count]);
+ JackInfoCommand cmd(start_id, info_count, reply.get());
+
+ executor.JacksInfo(cmd);
+ return CmdReply(cmd.status(), reply.get(),
+ info_count * sizeof(reply[0]));
+ }
case AudioCommandType::VIRTIO_SND_R_JACK_REMAP:
LOG(ERROR) << "Unsupported command type: " << cmd_hdr->code.as_uint32_t();
return CmdReply(AudioStatus::VIRTIO_SND_S_NOT_SUPP);
@@ -302,21 +333,15 @@
virtio_snd_hdr vio_status = {
.code = Le32(static_cast<uint32_t>(status)),
};
- auto status_sent = control_socket_->Send(&vio_status, sizeof(vio_status), 0);
- if (status_sent < sizeof(vio_status)) {
+ std::vector<uint8_t> buffer(sizeof(vio_status) + size, 0);
+ std::memcpy(buffer.data(), &vio_status, sizeof(vio_status));
+ std::memcpy(buffer.data() + sizeof(vio_status), data, size);
+ auto status_sent = control_socket_->Send(buffer.data(), buffer.size(), 0);
+ if (status_sent < sizeof(vio_status) + size) {
LOG(ERROR) << "Failed to send entire command status: "
<< control_socket_->StrError();
return false;
}
- if (status != AudioStatus::VIRTIO_SND_S_OK || size == 0) {
- return true;
- }
- auto payload_sent = control_socket_->Send(data, size, 0);
- if (payload_sent < size) {
- LOG(ERROR) << "Failed to send entire command response payload: "
- << control_socket_->StrError();
- return false;
- }
return true;
}
diff --git a/host/libs/audio_connector/server.h b/host/libs/audio_connector/server.h
index 4ba2e7c..6fa14d9 100644
--- a/host/libs/audio_connector/server.h
+++ b/host/libs/audio_connector/server.h
@@ -41,6 +41,8 @@
virtual void ReleaseStream(StreamControlCommand& cmd) = 0;
virtual void StartStream(StreamControlCommand& cmd) = 0;
virtual void StopStream(StreamControlCommand& cmd) = 0;
+ virtual void ChmapsInfo(ChmapInfoCommand& cmd) = 0;
+ virtual void JacksInfo(JackInfoCommand& cmd) = 0;
// Implementations must call buffer.SendStatus() before destroying the buffer
// to notify the other side of the release of the buffer. Failure to do so
diff --git a/host/libs/audio_connector/shm_layout.h b/host/libs/audio_connector/shm_layout.h
index 9148cb8..24000e6 100644
--- a/host/libs/audio_connector/shm_layout.h
+++ b/host/libs/audio_connector/shm_layout.h
@@ -51,7 +51,7 @@
NOT_SET = static_cast<uint32_t>(-1),
};
-enum class AudioStreamDirection : uint32_t {
+enum class AudioStreamDirection : uint8_t {
VIRTIO_SND_D_OUTPUT = 0,
VIRTIO_SND_D_INPUT
};
@@ -104,6 +104,47 @@
VIRTIO_SND_PCM_RATE_384000
};
+/* standard channel position definition */
+enum AudioChannelMap : uint8_t {
+ VIRTIO_SND_CHMAP_NONE = 0, /* undefined */
+ VIRTIO_SND_CHMAP_NA, /* silent */
+ VIRTIO_SND_CHMAP_MONO, /* mono stream */
+ VIRTIO_SND_CHMAP_FL, /* front left */
+ VIRTIO_SND_CHMAP_FR, /* front right */
+ VIRTIO_SND_CHMAP_RL, /* rear left */
+ VIRTIO_SND_CHMAP_RR, /* rear right */
+ VIRTIO_SND_CHMAP_FC, /* front center */
+ VIRTIO_SND_CHMAP_LFE, /* low frequency (LFE) */
+ VIRTIO_SND_CHMAP_SL, /* side left */
+ VIRTIO_SND_CHMAP_SR, /* side right */
+ VIRTIO_SND_CHMAP_RC, /* rear center */
+ VIRTIO_SND_CHMAP_FLC, /* front left center */
+ VIRTIO_SND_CHMAP_FRC, /* front right center */
+ VIRTIO_SND_CHMAP_RLC, /* rear left center */
+ VIRTIO_SND_CHMAP_RRC, /* rear right center */
+ VIRTIO_SND_CHMAP_FLW, /* front left wide */
+ VIRTIO_SND_CHMAP_FRW, /* front right wide */
+ VIRTIO_SND_CHMAP_FLH, /* front left high */
+ VIRTIO_SND_CHMAP_FCH, /* front center high */
+ VIRTIO_SND_CHMAP_FRH, /* front right high */
+ VIRTIO_SND_CHMAP_TC, /* top center */
+ VIRTIO_SND_CHMAP_TFL, /* top front left */
+ VIRTIO_SND_CHMAP_TFR, /* top front right */
+ VIRTIO_SND_CHMAP_TFC, /* top front center */
+ VIRTIO_SND_CHMAP_TRL, /* top rear left */
+ VIRTIO_SND_CHMAP_TRR, /* top rear right */
+ VIRTIO_SND_CHMAP_TRC, /* top rear center */
+ VIRTIO_SND_CHMAP_TFLC, /* top front left center */
+ VIRTIO_SND_CHMAP_TFRC, /* top front right center */
+ VIRTIO_SND_CHMAP_TSL, /* top side left */
+ VIRTIO_SND_CHMAP_TSR, /* top side right */
+ VIRTIO_SND_CHMAP_LLFE, /* left LFE */
+ VIRTIO_SND_CHMAP_RLFE, /* right LFE */
+ VIRTIO_SND_CHMAP_BC, /* bottom center */
+ VIRTIO_SND_CHMAP_BLC, /* bottom left center */
+ VIRTIO_SND_CHMAP_BRC /* bottom right center */
+};
+
struct virtio_snd_hdr {
Le32 code;
};
@@ -119,6 +160,29 @@
Le32 hda_fn_nid;
};
+/* supported jack features */
+enum AudioJackFeatures: uint8_t {
+ VIRTIO_SND_JACK_F_REMAP = 0
+};
+
+struct virtio_snd_jack_info {
+ struct virtio_snd_info hdr;
+ Le32 features; /* 1 << VIRTIO_SND_JACK_F_XXX */
+ Le32 hda_reg_defconf;
+ Le32 hda_reg_caps;
+ uint8_t connected;
+
+ uint8_t padding[7];
+};
+
+constexpr uint8_t VIRTIO_SND_CHMAP_MAX_SIZE = 18;
+struct virtio_snd_chmap_info {
+ struct virtio_snd_info hdr;
+ uint8_t direction;
+ uint8_t channels;
+ uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
+};
+
struct virtio_snd_pcm_info {
struct virtio_snd_info hdr;
Le32 features; /* 1 << VIRTIO_SND_PCM_F_XXX */
@@ -157,7 +221,7 @@
};
// Update this value when the msg layouts change
-const uint32_t VIOS_VERSION = 1;
+const uint32_t VIOS_VERSION = 2;
struct VioSConfig {
uint32_t version;
@@ -182,6 +246,8 @@
#define ASSERT_VALID_MSG_TYPE(T, size) \
static_assert(sizeof(T) == (size), #T " has the wrong size")
ASSERT_VALID_MSG_TYPE(virtio_snd_query_info, 16);
+ASSERT_VALID_MSG_TYPE(virtio_snd_jack_info, 24);
+ASSERT_VALID_MSG_TYPE(virtio_snd_chmap_info, 24);
ASSERT_VALID_MSG_TYPE(virtio_snd_pcm_info, 32);
ASSERT_VALID_MSG_TYPE(virtio_snd_pcm_set_params, 24);
ASSERT_VALID_MSG_TYPE(virtio_snd_pcm_hdr, 8);
@@ -189,4 +255,4 @@
ASSERT_VALID_MSG_TYPE(IoStatusMsg, 16);
#undef ASSERT_VALID_MSG_TYPE
-} // namespace cuttlefish
\ No newline at end of file
+} // namespace cuttlefish
diff --git a/host/libs/config/bootconfig_args.cpp b/host/libs/config/bootconfig_args.cpp
index c2bc1eb..1f926eb 100644
--- a/host/libs/config/bootconfig_args.cpp
+++ b/host/libs/config/bootconfig_args.cpp
@@ -90,7 +90,13 @@
bootconfig_args.push_back(
concat("androidboot.serialno=", instance.serial_number()));
- bootconfig_args.push_back(concat("androidboot.lcd_density=", config.dpi()));
+
+ // TODO(b/131884992): update to specify multiple once supported.
+ const auto display_configs = config.display_configs();
+ CHECK_GE(display_configs.size(), 1);
+ bootconfig_args.push_back(
+ concat("androidboot.lcd_density=", display_configs[0].dpi));
+
bootconfig_args.push_back(
concat("androidboot.setupwizard_mode=", config.setupwizard_mode()));
if (!config.guest_enforce_security()) {
@@ -143,6 +149,13 @@
instance.frames_server_port()));
}
+ if (instance.camera_server_port()) {
+ bootconfig_args.push_back(concat("androidboot.vsock_camera_port=",
+ instance.camera_server_port()));
+ bootconfig_args.push_back(
+ concat("androidboot.vsock_camera_cid=", instance.vsock_guest_cid()));
+ }
+
if (config.enable_modem_simulator() &&
instance.modem_simulator_ports() != "") {
bootconfig_args.push_back(concat("androidboot.modem_simulator_ports=",
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index c04faae..b5160d0 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -123,13 +123,11 @@
(*dictionary_)[kMemoryMb] = memory_mb;
}
-static constexpr char kDpi[] = "dpi";
-int CuttlefishConfig::dpi() const { return (*dictionary_)[kDpi].asInt(); }
-void CuttlefishConfig::set_dpi(int dpi) { (*dictionary_)[kDpi] = dpi; }
-
static constexpr char kDisplayConfigs[] = "display_configs";
static constexpr char kXRes[] = "x_res";
static constexpr char kYRes[] = "y_res";
+static constexpr char kDpi[] = "dpi";
+static constexpr char kRefreshRateHz[] = "refresh_rate_hz";
std::vector<CuttlefishConfig::DisplayConfig>
CuttlefishConfig::display_configs() const {
std::vector<DisplayConfig> display_configs;
@@ -137,6 +135,9 @@
DisplayConfig display_config = {};
display_config.width = display_config_json[kXRes].asInt();
display_config.height = display_config_json[kYRes].asInt();
+ display_config.dpi = display_config_json[kDpi].asInt();
+ display_config.refresh_rate_hz =
+ display_config_json[kRefreshRateHz].asInt();
display_configs.emplace_back(std::move(display_config));
}
return display_configs;
@@ -149,20 +150,14 @@
Json::Value display_config_json(Json::objectValue);
display_config_json[kXRes] = display_configs.width;
display_config_json[kYRes] = display_configs.height;
+ display_config_json[kDpi] = display_configs.dpi;
+ display_config_json[kRefreshRateHz] = display_configs.refresh_rate_hz;
display_configs_json.append(display_config_json);
}
(*dictionary_)[kDisplayConfigs] = display_configs_json;
}
-static constexpr char kRefreshRateHz[] = "refresh_rate_hz";
-int CuttlefishConfig::refresh_rate_hz() const {
- return (*dictionary_)[kRefreshRateHz].asInt();
-}
-void CuttlefishConfig::set_refresh_rate_hz(int refresh_rate_hz) {
- (*dictionary_)[kRefreshRateHz] = refresh_rate_hz;
-}
-
void CuttlefishConfig::SetPath(const std::string& key,
const std::string& path) {
if (!path.empty()) {
@@ -695,14 +690,6 @@
return (*dictionary_)[kVhostNet].asBool();
}
-static constexpr char kEthernet[] = "ethernet";
-void CuttlefishConfig::set_ethernet(bool ethernet) {
- (*dictionary_)[kEthernet] = ethernet;
-}
-bool CuttlefishConfig::ethernet() const {
- return (*dictionary_)[kEthernet].asBool();
-}
-
static constexpr char kRecordScreen[] = "record_screen";
void CuttlefishConfig::set_record_screen(bool record_screen) {
(*dictionary_)[kRecordScreen] = record_screen;
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 2b76c75..db3207e 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -52,6 +52,8 @@
constexpr char kEthernetConnectedMessage[] =
"VIRTUAL_DEVICE_NETWORK_ETHERNET_CONNECTED";
constexpr char kScreenChangedMessage[] = "VIRTUAL_DEVICE_SCREEN_CHANGED";
+constexpr char kDisplayPowerModeChangedMessage[] =
+ "VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED";
constexpr char kInternalDirName[] = "internal";
constexpr char kSharedDirName[] = "shared";
constexpr char kCrosvmVarEmptyDir[] = "/var/empty";
@@ -101,15 +103,11 @@
int memory_mb() const;
void set_memory_mb(int memory_mb);
- int dpi() const;
- void set_dpi(int dpi);
-
- int refresh_rate_hz() const;
- void set_refresh_rate_hz(int refresh_rate_hz);
-
struct DisplayConfig {
int width;
int height;
+ int dpi;
+ int refresh_rate_hz;
};
std::vector<DisplayConfig> display_configs() const;
@@ -294,9 +292,6 @@
void set_vhost_net(bool vhost_net);
bool vhost_net() const;
- void set_ethernet(bool ethernet);
- bool ethernet() const;
-
void set_record_screen(bool record_screen);
bool record_screen() const;
@@ -370,6 +365,8 @@
int rootcanal_hci_port() const;
int rootcanal_link_port() const;
int rootcanal_test_port() const;
+ // Port number to connect to the camera hal on the guest
+ int camera_server_port() const;
std::string rootcanal_config_file() const;
std::string rootcanal_default_commands_file() const;
@@ -396,7 +393,7 @@
std::string instance_internal_dir() const;
- std::string touch_socket_path() const;
+ std::string touch_socket_path(int screen_idx) const;
std::string keyboard_socket_path() const;
std::string switches_socket_path() const;
std::string frames_socket_path() const;
@@ -485,6 +482,7 @@
void set_rootcanal_hci_port(int rootcanal_hci_port);
void set_rootcanal_link_port(int rootcanal_link_port);
void set_rootcanal_test_port(int rootcanal_test_port);
+ void set_camera_server_port(int camera_server_port);
void set_rootcanal_config_file(const std::string& rootcanal_config_file);
void set_rootcanal_default_commands_file(
const std::string& rootcanal_default_commands_file);
diff --git a/host/libs/config/cuttlefish_config_instance.cpp b/host/libs/config/cuttlefish_config_instance.cpp
index 97b3503..14e7037 100644
--- a/host/libs/config/cuttlefish_config_instance.cpp
+++ b/host/libs/config/cuttlefish_config_instance.cpp
@@ -389,6 +389,15 @@
(*Dictionary())[kRootcanalTestPort] = rootcanal_test_port;
}
+static constexpr char kCameraServerPort[] = "camera_server_port";
+int CuttlefishConfig::InstanceSpecific::camera_server_port() const {
+ return (*Dictionary())[kCameraServerPort].asInt();
+}
+void CuttlefishConfig::MutableInstanceSpecific::set_camera_server_port(
+ int camera_server_port) {
+ (*Dictionary())[kCameraServerPort] = camera_server_port;
+}
+
static constexpr char kRootcanalConfigFile[] = "rootcanal_config_file";
std::string CuttlefishConfig::InstanceSpecific::rootcanal_config_file() const {
return (*Dictionary())[kRootcanalConfigFile].asString();
@@ -429,8 +438,10 @@
return (*Dictionary())[kStartSigServer].asBool();
}
-std::string CuttlefishConfig::InstanceSpecific::touch_socket_path() const {
- return PerInstanceInternalPath("touch.sock");
+std::string CuttlefishConfig::InstanceSpecific::touch_socket_path(
+ int screen_idx) const {
+ return PerInstanceInternalPath(
+ ("touch_" + std::to_string(screen_idx) + ".sock").c_str());
}
std::string CuttlefishConfig::InstanceSpecific::keyboard_socket_path() const {
diff --git a/host/libs/config/kernel_args.cpp b/host/libs/config/kernel_args.cpp
index 421950a..f5393d5 100644
--- a/host/libs/config/kernel_args.cpp
+++ b/host/libs/config/kernel_args.cpp
@@ -80,7 +80,7 @@
vm_manager_cmdline.push_back("ramoops.dump_oops=1");
} else {
// crosvm requires these additional parameters on x86_64 in bootloader mode
- AppendVector(&vm_manager_cmdline, {"pci=noacpi", "reboot=k"});
+ AppendVector(&vm_manager_cmdline, {"reboot=k"});
}
}
}
diff --git a/host/libs/confui/session.cc b/host/libs/confui/session.cc
index 3d365c6..891250d 100644
--- a/host/libs/confui/session.cc
+++ b/host/libs/confui/session.cc
@@ -50,8 +50,13 @@
ConfUiLog(DEBUG) << "actually trying to render the frame"
<< thread::GetName();
- auto raw_frame = reinterpret_cast<std::uint8_t*>(teeui_frame.data());
- return screen_connector_.RenderConfirmationUi(display_num_, raw_frame);
+ auto frame_width = ScreenConnectorInfo::ScreenWidth(display_num_);
+ auto frame_height = ScreenConnectorInfo::ScreenHeight(display_num_);
+ auto frame_stride_bytes =
+ ScreenConnectorInfo::ScreenStrideBytes(display_num_);
+ auto frame_bytes = reinterpret_cast<std::uint8_t*>(teeui_frame.data());
+ return screen_connector_.RenderConfirmationUi(
+ display_num_, frame_width, frame_height, frame_stride_bytes, frame_bytes);
}
bool Session::IsSuspended() const {
diff --git a/host/libs/screen_connector/screen_connector.h b/host/libs/screen_connector/screen_connector.h
index de55b0e..ca6b155 100644
--- a/host/libs/screen_connector/screen_connector.h
+++ b/host/libs/screen_connector/screen_connector.h
@@ -64,7 +64,9 @@
* call.
*/
using GenerateProcessedFrameCallback = std::function<void(
- std::uint32_t /*display_number*/, std::uint8_t* /*frame_pixels*/,
+ std::uint32_t /*display_number*/, std::uint32_t /*frame_width*/,
+ std::uint32_t /*frame_height*/, std::uint32_t /*frame_stride_bytes*/,
+ std::uint8_t* /*frame_bytes*/,
/* ScImpl enqueues this type into the Q */
ProcessedFrameType& msg)>;
@@ -94,14 +96,27 @@
std::lock_guard<std::mutex> lock(streamer_callback_mutex_);
callback_from_streamer_ = std::move(frame_callback);
streamer_callback_set_cv_.notify_all();
- /*
- * the first WaitForAtLeastOneClientConnection() call from VNC requires the
- * Android-frame-processing thread starts beforehands (b/178504150)
- */
- if (!sc_android_frame_fetching_thread_.joinable()) {
- sc_android_frame_fetching_thread_ = cuttlefish::confui::thread::RunThread(
- "AndroidFetcher", &ScreenConnector::AndroidFrameFetchingLoop, this);
- }
+
+ sc_android_src_->SetFrameCallback(
+ [this](std::uint32_t display_number, std::uint32_t frame_w,
+ std::uint32_t frame_h, std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_bytes) {
+ const bool is_confui_mode = host_mode_ctrl_.IsConfirmatioUiMode();
+ if (is_confui_mode) {
+ return;
+ }
+
+ ProcessedFrameType processed_frame;
+
+ {
+ std::lock_guard<std::mutex> lock(streamer_callback_mutex_);
+ callback_from_streamer_(display_number, frame_w, frame_h,
+ frame_stride_bytes, frame_bytes,
+ processed_frame);
+ }
+
+ sc_android_queue_.PushBack(std::move(processed_frame));
+ });
}
bool IsCallbackSet() const override {
@@ -155,43 +170,6 @@
}
}
- [[noreturn]] void AndroidFrameFetchingLoop() {
- unsigned long long int loop_cnt = 0;
- cuttlefish::confui::thread::Set("AndroidFrameFetcher",
- std::this_thread::get_id());
- while (true) {
- loop_cnt++;
- ProcessedFrameType processed_frame;
- decltype(callback_from_streamer_) cp_of_streamer_callback;
- {
- std::lock_guard<std::mutex> lock(streamer_callback_mutex_);
- cp_of_streamer_callback = callback_from_streamer_;
- }
- GenerateProcessedFrameCallbackImpl callback_for_sc_impl =
- std::bind(cp_of_streamer_callback, std::placeholders::_1,
- std::placeholders::_2, std::ref(processed_frame));
- ConfUiLog(VERBOSE) << cuttlefish::confui::thread::GetName(
- std::this_thread::get_id())
- << " calling Android OnNextFrame. "
- << " at loop #" << loop_cnt;
- bool flag = sc_android_src_->OnNextFrame(callback_for_sc_impl);
- processed_frame.is_success_ = flag && processed_frame.is_success_;
- const bool is_confui_mode = host_mode_ctrl_.IsConfirmatioUiMode();
- if (!is_confui_mode) {
- ConfUiLog(VERBOSE) << cuttlefish::confui::thread::GetName(
- std::this_thread::get_id())
- << "is sending an Android Frame at loop_cnt #"
- << loop_cnt;
- sc_android_queue_.PushBack(std::move(processed_frame));
- continue;
- }
- ConfUiLog(VERBOSE) << cuttlefish::confui::thread::GetName(
- std::this_thread::get_id())
- << "is skipping an Android Frame at loop_cnt #"
- << loop_cnt;
- }
- }
-
/**
* ConfUi calls this when it has frames to render
*
@@ -199,8 +177,11 @@
* Android guest frames if Confirmation UI HAL is not active.
*
*/
- bool RenderConfirmationUi(const std::uint32_t display,
- std::uint8_t* raw_frame) override {
+ bool RenderConfirmationUi(std::uint32_t display_number,
+ std::uint32_t frame_width,
+ std::uint32_t frame_height,
+ std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_bytes) override {
render_confui_cnt_++;
// wait callback is not set, the streamer is not ready
// return with LOG(ERROR)
@@ -213,7 +194,8 @@
ConfUiLog(DEBUG) << this_thread_name
<< "is sending a #" + std::to_string(render_confui_cnt_)
<< "Conf UI frame";
- callback_from_streamer_(display, raw_frame, processed_frame);
+ callback_from_streamer_(display_number, frame_width, frame_height,
+ frame_stride_bytes, frame_bytes, processed_frame);
// now add processed_frame to the queue
sc_confui_queue_.PushBack(std::move(processed_frame));
return true;
@@ -249,7 +231,6 @@
ScreenConnectorQueue<ProcessedFrameType> sc_android_queue_;
ScreenConnectorQueue<ProcessedFrameType> sc_confui_queue_;
GenerateProcessedFrameCallback callback_from_streamer_;
- std::thread sc_android_frame_fetching_thread_;
std::mutex streamer_callback_mutex_; // mutex to set & read callback_from_streamer_
std::condition_variable streamer_callback_set_cv_;
};
diff --git a/host/libs/screen_connector/screen_connector_common.h b/host/libs/screen_connector/screen_connector_common.h
index 48c7c76..1df6d86 100644
--- a/host/libs/screen_connector/screen_connector_common.h
+++ b/host/libs/screen_connector/screen_connector_common.h
@@ -34,16 +34,20 @@
};
// this callback type is going directly to socket-based or wayland ScreenConnector
-using GenerateProcessedFrameCallbackImpl = std::function<void(std::uint32_t /*display_number*/,
- std::uint8_t* /*frame_pixels*/)>;
+using GenerateProcessedFrameCallbackImpl =
+ std::function<void(std::uint32_t /*display_number*/, //
+ std::uint32_t /*frame_width*/, //
+ std::uint32_t /*frame_height*/, //
+ std::uint32_t /*frame_stride_bytes*/, //
+ std::uint8_t* /*frame_pixels*/)>;
class ScreenConnectorSource {
public:
virtual ~ScreenConnectorSource() = default;
// Runs the given callback on the next available frame after the given
// frame number and returns true if successful.
- virtual bool OnNextFrame(
- const GenerateProcessedFrameCallbackImpl& frame_callback) = 0;
+ virtual void SetFrameCallback(
+ GenerateProcessedFrameCallbackImpl frame_callback) = 0;
virtual void ReportClientsConnected(bool /*have_clients*/) { /* ignore by default */ }
ScreenConnectorSource() = default;
};
@@ -83,8 +87,11 @@
};
struct ScreenConnectorFrameRenderer {
- virtual bool RenderConfirmationUi(const std::uint32_t display,
- std::uint8_t* raw_frame) = 0;
+ virtual bool RenderConfirmationUi(std::uint32_t display_number,
+ std::uint32_t frame_width,
+ std::uint32_t frame_height,
+ std::uint32_t frame_stride_bytes,
+ std::uint8_t* frame_bytes) = 0;
virtual bool IsCallbackSet() const = 0;
virtual ~ScreenConnectorFrameRenderer() = default;
};
diff --git a/host/libs/screen_connector/wayland_screen_connector.cpp b/host/libs/screen_connector/wayland_screen_connector.cpp
index 7eb6642..dbd4052 100644
--- a/host/libs/screen_connector/wayland_screen_connector.cpp
+++ b/host/libs/screen_connector/wayland_screen_connector.cpp
@@ -33,10 +33,9 @@
server_.reset(new wayland::WaylandServer(wayland_fd));
}
-bool WaylandScreenConnector::OnNextFrame(
- const GenerateProcessedFrameCallbackImpl& frame_callback) {
- server_->OnNextFrame(frame_callback);
- return true;
+void WaylandScreenConnector::SetFrameCallback(
+ GenerateProcessedFrameCallbackImpl frame_callback) {
+ server_->SetFrameCallback(std::move(frame_callback));
}
} // namespace cuttlefish
diff --git a/host/libs/screen_connector/wayland_screen_connector.h b/host/libs/screen_connector/wayland_screen_connector.h
index 00e7ca0..36ed322 100644
--- a/host/libs/screen_connector/wayland_screen_connector.h
+++ b/host/libs/screen_connector/wayland_screen_connector.h
@@ -28,8 +28,8 @@
public:
WaylandScreenConnector(int frames_fd);
- bool OnNextFrame(
- const GenerateProcessedFrameCallbackImpl& frame_callback) override;
+ void SetFrameCallback(
+ GenerateProcessedFrameCallbackImpl frame_callback) override;
private:
std::unique_ptr<wayland::WaylandServer> server_;
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index 82aba2d..e424fd5 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -220,23 +220,20 @@
crosvm_cmd.AddParameter("--gdb=", config.gdb_port());
}
- auto display_configs = config.display_configs();
- CHECK_GE(display_configs.size(), 1);
- auto display_config = display_configs[0];
-
auto gpu_mode = config.gpu_mode();
-
if (gpu_mode == kGpuModeGuestSwiftshader) {
- crosvm_cmd.AddParameter("--gpu=2D,",
- "width=", display_config.width, ",",
- "height=", display_config.height);
+ crosvm_cmd.AddParameter("--gpu=2D");
} else if (gpu_mode == kGpuModeDrmVirgl || gpu_mode == kGpuModeGfxStream) {
crosvm_cmd.AddParameter(gpu_mode == kGpuModeGfxStream ?
"--gpu=gfxstream," : "--gpu=",
- "width=", display_config.width, ",",
- "height=", display_config.height, ",",
"egl=true,surfaceless=true,glx=false,gles=true");
}
+
+ for (const auto& display_config : config.display_configs()) {
+ crosvm_cmd.AddParameter("--gpu-display=", "width=", display_config.width,
+ ",", "height=", display_config.height);
+ }
+
crosvm_cmd.AddParameter("--wayland-sock=", instance.frames_socket_path());
// crosvm_cmd.AddParameter("--null-audio");
@@ -256,17 +253,26 @@
if (config.enable_vnc_server() || config.enable_webrtc()) {
auto touch_type_parameter =
config.enable_webrtc() ? "--multi-touch=" : "--single-touch=";
- crosvm_cmd.AddParameter(touch_type_parameter, instance.touch_socket_path(),
- ":", display_config.width, ":",
- display_config.height);
+
+ auto display_configs = config.display_configs();
+ CHECK_GE(display_configs.size(), 1);
+
+ for (int i = 0; i < display_configs.size(); ++i) {
+ auto display_config = display_configs[i];
+
+ crosvm_cmd.AddParameter(touch_type_parameter,
+ instance.touch_socket_path(i), ":",
+ display_config.width, ":", display_config.height);
+ }
crosvm_cmd.AddParameter("--keyboard=", instance.keyboard_socket_path());
}
if (config.enable_webrtc()) {
crosvm_cmd.AddParameter("--switches=", instance.switches_socket_path());
}
- auto wifi_tap = AddTapFdParameter(&crosvm_cmd, instance.wifi_tap_name());
AddTapFdParameter(&crosvm_cmd, instance.mobile_tap_name());
+ AddTapFdParameter(&crosvm_cmd, instance.ethernet_tap_name());
+ auto wifi_tap = AddTapFdParameter(&crosvm_cmd, instance.wifi_tap_name());
if (FileExists(instance.access_kregistry_path())) {
crosvm_cmd.AddParameter("--rw-pmem-device=",
@@ -376,16 +382,10 @@
<< VmManager::kMaxDisks + VmManager::kDefaultNumHvcs << " devices";
if (config.enable_audio()) {
- crosvm_cmd.AddParameter("--ac97=backend=vios,server=" +
+ crosvm_cmd.AddParameter("--sound=",
config.ForDefaultInstance().audio_server_path());
}
- // TODO(b/172286896): This is temporarily optional, but should be made
- // unconditional and moved up to the other network devices area
- if (config.ethernet()) {
- AddTapFdParameter(&crosvm_cmd, instance.ethernet_tap_name());
- }
-
// TODO(b/162071003): virtiofs crashes without sandboxing, this should be fixed
if (config.enable_sandbox()) {
// Set up directory shared with virtiofs
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index f26818a..c8c1084 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -432,19 +432,26 @@
qemu_cmd.AddParameter("virtio-balloon-pci-non-transitional,id=balloon0");
qemu_cmd.AddParameter("-netdev");
- qemu_cmd.AddParameter("tap,id=hostnet0,ifname=", instance.wifi_tap_name(),
+ qemu_cmd.AddParameter("tap,id=hostnet0,ifname=", instance.mobile_tap_name(),
",script=no,downscript=no", vhost_net);
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet0,id=net0");
qemu_cmd.AddParameter("-netdev");
- qemu_cmd.AddParameter("tap,id=hostnet1,ifname=", instance.mobile_tap_name(),
+ qemu_cmd.AddParameter("tap,id=hostnet1,ifname=", instance.ethernet_tap_name(),
",script=no,downscript=no", vhost_net);
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet1,id=net1");
+ qemu_cmd.AddParameter("-netdev");
+ qemu_cmd.AddParameter("tap,id=hostnet2,ifname=", instance.wifi_tap_name(),
+ ",script=no,downscript=no", vhost_net);
+
+ qemu_cmd.AddParameter("-device");
+ qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet2,id=net2");
+
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("virtio-gpu-pci,id=gpu0");
@@ -461,17 +468,6 @@
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("AC97");
- // TODO(b/172286896): This is temporarily optional, but should be made
- // unconditional and moved up to the other network devices area
- if (config.ethernet()) {
- qemu_cmd.AddParameter("-netdev");
- qemu_cmd.AddParameter("tap,id=hostnet2,ifname=", instance.ethernet_tap_name(),
- ",script=no,downscript=no", vhost_net);
-
- qemu_cmd.AddParameter("-device");
- qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet2,id=net2");
- }
-
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("qemu-xhci,id=xhci");
diff --git a/host/libs/wayland/Android.bp b/host/libs/wayland/Android.bp
index 4be6528..12700f3 100644
--- a/host/libs/wayland/Android.bp
+++ b/host/libs/wayland/Android.bp
@@ -28,6 +28,7 @@
"wayland_subcompositor.cpp",
"wayland_surface.cpp",
"wayland_surfaces.cpp",
+ "wayland_virtio_gpu_metadata.cpp",
],
shared_libs: [
"libbase",
@@ -37,6 +38,7 @@
static_libs: [
"libdrm",
"libffi",
+ "libwayland_crosvm_gpu_display_extension_server_protocols",
"libwayland_server",
"libwayland_extension_server_protocols",
],
diff --git a/host/libs/wayland/wayland_compositor.cpp b/host/libs/wayland/wayland_compositor.cpp
index 6179092..4e2f8f5 100644
--- a/host/libs/wayland/wayland_compositor.cpp
+++ b/host/libs/wayland/wayland_compositor.cpp
@@ -182,7 +182,10 @@
.damage_buffer = surface_damage_buffer,
};
-void surface_destroy_resource_callback(struct wl_resource*) {}
+void surface_destroy_resource_callback(struct wl_resource* surface_resource) {
+ Surface* surface = GetUserData<Surface>(surface_resource);
+ delete surface;
+}
void compositor_create_surface(wl_client* client,
wl_resource* compositor,
@@ -191,12 +194,8 @@
<< " compositor=" << compositor
<< " id=" << id;
- // Wayland seems to use a single global id space for all objects.
- static std::atomic<std::uint32_t> sNextDisplayId{0};
- uint32_t display_id = sNextDisplayId++;
-
Surfaces* surfaces = GetUserData<Surfaces>(compositor);
- Surface* surface = surfaces->GetOrCreateSurface(display_id);
+ Surface* surface = new Surface(*surfaces);
wl_resource* surface_resource = wl_resource_create(
client, &wl_surface_interface, wl_resource_get_version(compositor), id);
diff --git a/host/libs/wayland/wayland_server.cpp b/host/libs/wayland/wayland_server.cpp
index 4dc4b88..7dbc7f7 100644
--- a/host/libs/wayland/wayland_server.cpp
+++ b/host/libs/wayland/wayland_server.cpp
@@ -28,6 +28,7 @@
#include "host/libs/wayland/wayland_subcompositor.h"
#include "host/libs/wayland/wayland_surface.h"
#include "host/libs/wayland/wayland_utils.h"
+#include "host/libs/wayland/wayland_virtio_gpu_metadata.h"
namespace wayland {
namespace internal {
@@ -79,6 +80,8 @@
wl_display_init_shm(server_state_->display_);
BindCompositorInterface(server_state_->display_, &server_state_->surfaces_);
+ BindVirtioGpuMetadataInterface(server_state_->display_,
+ &server_state_->surfaces_);
BindDmabufInterface(server_state_->display_);
BindSubcompositorInterface(server_state_->display_);
BindSeatInterface(server_state_->display_);
@@ -94,8 +97,8 @@
wl_display_destroy(server_state_->display_);
}
-void WaylandServer::OnNextFrame(const Surfaces::FrameCallback& callback) {
- server_state_->surfaces_.OnNextFrame(callback);
+void WaylandServer::SetFrameCallback(Surfaces::FrameCallback callback) {
+ server_state_->surfaces_.SetFrameCallback(std::move(callback));
}
} // namespace wayland
diff --git a/host/libs/wayland/wayland_server.h b/host/libs/wayland/wayland_server.h
index 3149481..24ee68c 100644
--- a/host/libs/wayland/wayland_server.h
+++ b/host/libs/wayland/wayland_server.h
@@ -48,8 +48,9 @@
WaylandServer(WaylandServer&& rhs) = delete;
WaylandServer& operator=(WaylandServer&& rhs) = delete;
- // Blocks until the given callback is run on the next frame available.
- void OnNextFrame(const Surfaces::FrameCallback& callback);
+ // Registers the callback that will be run whenever a new frame is
+ // available.
+ void SetFrameCallback(Surfaces::FrameCallback callback);
private:
void ServerLoop(int wayland_socket_fd);
diff --git a/host/libs/wayland/wayland_shell.cpp b/host/libs/wayland/wayland_shell.cpp
index 439fc62..424c878 100644
--- a/host/libs/wayland/wayland_shell.cpp
+++ b/host/libs/wayland/wayland_shell.cpp
@@ -20,35 +20,29 @@
#include <wayland-server-core.h>
#include <wayland-server-protocol.h>
-#include <xdg-shell-unstable-v6-server-protocol.h>
+#include <xdg-shell-server-protocol.h>
namespace wayland {
namespace {
-
-void zxdg_positioner_v6_destroy(wl_client*, wl_resource* positioner) {
+void xdg_positioner_destroy(wl_client*, wl_resource* positioner) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner;
wl_resource_destroy(positioner);
}
-void zxdg_positioner_v6_set_size(wl_client*,
- wl_resource* positioner,
- int32_t w,
- int32_t h) {
+void xdg_positioner_set_size(wl_client*, wl_resource* positioner, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " w=" << w
<< " h=" << h;
}
-void zxdg_positioner_v6_set_anchor_rect(wl_client*,
- wl_resource* positioner,
- int32_t x,
- int32_t y,
- int32_t w,
- int32_t h) {
+void xdg_positioner_set_anchor_rect(wl_client*, wl_resource* positioner,
+ int32_t x, int32_t y, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " x=" << x
@@ -57,87 +51,76 @@
<< " h=" << h;
}
-void zxdg_positioner_v6_set_anchor(wl_client*,
- wl_resource* positioner,
- uint32_t anchor) {
+void xdg_positioner_set_anchor(wl_client*, wl_resource* positioner,
+ uint32_t anchor) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " anchor=" << anchor;
}
-void zxdg_positioner_v6_set_gravity(wl_client*,
- wl_resource* positioner,
- uint32_t gravity) {
+void xdg_positioner_set_gravity(wl_client*, wl_resource* positioner,
+ uint32_t gravity) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " gravity=" << gravity;
}
-void zxdg_positioner_v6_set_constraint_adjustment(wl_client*,
- wl_resource* positioner,
- uint32_t adjustment) {
+void xdg_positioner_set_constraint_adjustment(wl_client*,
+ wl_resource* positioner,
+ uint32_t adjustment) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " adjustment=" << adjustment;
}
-void zxdg_positioner_v6_set_offset(wl_client*,
- wl_resource* positioner,
- int32_t x,
- int32_t y) {
+void xdg_positioner_set_offset(wl_client*, wl_resource* positioner, int32_t x,
+ int32_t y) {
LOG(VERBOSE) << __FUNCTION__
<< " positioner=" << positioner
<< " x=" << x
<< " y=" << y;
}
-const struct zxdg_positioner_v6_interface
- zxdg_positioner_v6_implementation = {
- .destroy = zxdg_positioner_v6_destroy,
- .set_size = zxdg_positioner_v6_set_size,
- .set_anchor_rect = zxdg_positioner_v6_set_anchor_rect,
- .set_anchor = zxdg_positioner_v6_set_anchor,
- .set_gravity = zxdg_positioner_v6_set_gravity,
- .set_constraint_adjustment = zxdg_positioner_v6_set_constraint_adjustment,
- .set_offset = zxdg_positioner_v6_set_offset};
+const struct xdg_positioner_interface xdg_positioner_implementation = {
+ .destroy = xdg_positioner_destroy,
+ .set_size = xdg_positioner_set_size,
+ .set_anchor_rect = xdg_positioner_set_anchor_rect,
+ .set_anchor = xdg_positioner_set_anchor,
+ .set_gravity = xdg_positioner_set_gravity,
+ .set_constraint_adjustment = xdg_positioner_set_constraint_adjustment,
+ .set_offset = xdg_positioner_set_offset};
-void zxdg_toplevel_v6_destroy(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_destroy(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
wl_resource_destroy(toplevel);
}
-void zxdg_toplevel_v6_set_parent(wl_client*,
- wl_resource* toplevel,
- wl_resource* parent_toplevel) {
+void xdg_toplevel_set_parent(wl_client*, wl_resource* toplevel,
+ wl_resource* parent_toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " parent_toplevel=" << parent_toplevel;
}
-void zxdg_toplevel_v6_set_title(wl_client*,
- wl_resource* toplevel,
- const char* title) {
+void xdg_toplevel_set_title(wl_client*, wl_resource* toplevel,
+ const char* title) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " title=" << title;
}
-void zxdg_toplevel_v6_set_app_id(wl_client*,
- wl_resource* toplevel,
- const char* app) {
+void xdg_toplevel_set_app_id(wl_client*, wl_resource* toplevel,
+ const char* app) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " app=" << app;
}
-void zxdg_toplevel_v6_show_window_menu(wl_client*,
- wl_resource* toplevel,
- wl_resource* seat,
- uint32_t serial,
- int32_t x,
- int32_t y) {
+void xdg_toplevel_show_window_menu(wl_client*, wl_resource* toplevel,
+ wl_resource* seat, uint32_t serial,
+ int32_t x, int32_t y) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " seat=" << seat
@@ -146,21 +129,16 @@
<< " y=" << y;
}
-void zxdg_toplevel_v6_move(wl_client*,
- wl_resource* toplevel,
- wl_resource* seat,
- uint32_t serial) {
+void xdg_toplevel_move(wl_client*, wl_resource* toplevel, wl_resource* seat,
+ uint32_t serial) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " seat=" << seat
<< " serial=" << serial;
}
-void zxdg_toplevel_v6_resize(wl_client*,
- wl_resource* toplevel,
- wl_resource* seat,
- uint32_t serial,
- uint32_t edges) {
+void xdg_toplevel_resize(wl_client*, wl_resource* toplevel, wl_resource* seat,
+ uint32_t serial, uint32_t edges) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " seat=" << seat
@@ -168,93 +146,83 @@
<< " edges=" << edges;
}
-void zxdg_toplevel_v6_set_max_size(wl_client*,
- wl_resource* toplevel,
- int32_t w,
- int32_t h) {
+void xdg_toplevel_set_max_size(wl_client*, wl_resource* toplevel, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " w=" << w
<< " h=" << h;
}
-void zxdg_toplevel_v6_set_min_size(wl_client*,
- wl_resource* toplevel,
- int32_t w,
- int32_t h) {
+void xdg_toplevel_set_min_size(wl_client*, wl_resource* toplevel, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel
<< " w=" << w
<< " h=" << h;
}
-void zxdg_toplevel_v6_set_maximized(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_set_maximized(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-void zxdg_toplevel_v6_unset_maximized(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_unset_maximized(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-void zxdg_toplevel_v6_set_fullscreen(wl_client*,
- wl_resource* toplevel,
- wl_resource*) {
+void xdg_toplevel_set_fullscreen(wl_client*, wl_resource* toplevel,
+ wl_resource*) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-void zxdg_toplevel_v6_unset_fullscreen(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_unset_fullscreen(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-void zxdg_toplevel_v6_set_minimized(wl_client*, wl_resource* toplevel) {
+void xdg_toplevel_set_minimized(wl_client*, wl_resource* toplevel) {
LOG(VERBOSE) << __FUNCTION__
<< " toplevel=" << toplevel;
}
-const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation = {
- .destroy = zxdg_toplevel_v6_destroy,
- .set_parent = zxdg_toplevel_v6_set_parent,
- .set_title = zxdg_toplevel_v6_set_title,
- .set_app_id = zxdg_toplevel_v6_set_app_id,
- .show_window_menu = zxdg_toplevel_v6_show_window_menu,
- .move = zxdg_toplevel_v6_move,
- .resize = zxdg_toplevel_v6_resize,
- .set_max_size = zxdg_toplevel_v6_set_max_size,
- .set_min_size = zxdg_toplevel_v6_set_min_size,
- .set_maximized = zxdg_toplevel_v6_set_maximized,
- .unset_maximized = zxdg_toplevel_v6_unset_maximized,
- .set_fullscreen = zxdg_toplevel_v6_set_fullscreen,
- .unset_fullscreen = zxdg_toplevel_v6_unset_fullscreen,
- .set_minimized = zxdg_toplevel_v6_set_minimized
-};
+const struct xdg_toplevel_interface xdg_toplevel_implementation = {
+ .destroy = xdg_toplevel_destroy,
+ .set_parent = xdg_toplevel_set_parent,
+ .set_title = xdg_toplevel_set_title,
+ .set_app_id = xdg_toplevel_set_app_id,
+ .show_window_menu = xdg_toplevel_show_window_menu,
+ .move = xdg_toplevel_move,
+ .resize = xdg_toplevel_resize,
+ .set_max_size = xdg_toplevel_set_max_size,
+ .set_min_size = xdg_toplevel_set_min_size,
+ .set_maximized = xdg_toplevel_set_maximized,
+ .unset_maximized = xdg_toplevel_unset_maximized,
+ .set_fullscreen = xdg_toplevel_set_fullscreen,
+ .unset_fullscreen = xdg_toplevel_unset_fullscreen,
+ .set_minimized = xdg_toplevel_set_minimized};
-void zxdg_popup_v6_destroy(wl_client*, wl_resource* popup) {
+void xdg_popup_destroy(wl_client*, wl_resource* popup) {
LOG(VERBOSE) << __FUNCTION__
<< " popup=" << popup;
wl_resource_destroy(popup);
}
-void zxdg_popup_v6_grab(wl_client*,
- wl_resource* popup,
- wl_resource* seat,
- uint32_t serial) {
+void xdg_popup_grab(wl_client*, wl_resource* popup, wl_resource* seat,
+ uint32_t serial) {
LOG(VERBOSE) << __FUNCTION__
<< " popup=" << popup
<< " seat=" << seat
<< " serial=" << serial;
}
-const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation = {
- .destroy = zxdg_popup_v6_destroy,
- .grab = zxdg_popup_v6_grab
-};
+const struct xdg_popup_interface xdg_popup_implementation = {
+ .destroy = xdg_popup_destroy, .grab = xdg_popup_grab};
-void zxdg_surface_v6_destroy(wl_client*, wl_resource* surface) {
+void xdg_surface_destroy(wl_client*, wl_resource* surface) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface;
@@ -263,28 +231,25 @@
void toplevel_destroy_resource_callback(struct wl_resource*) {}
-void zxdg_surface_v6_get_toplevel(wl_client* client,
- wl_resource* surface,
- uint32_t id) {
+void xdg_surface_get_toplevel(wl_client* client, wl_resource* surface,
+ uint32_t id) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface
<< " id=" << id;
wl_resource* xdg_toplevel_resource =
- wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
+ wl_resource_create(client, &xdg_toplevel_interface, 1, id);
wl_resource_set_implementation(xdg_toplevel_resource,
- &zxdg_toplevel_v6_implementation, nullptr,
+ &xdg_toplevel_implementation, nullptr,
toplevel_destroy_resource_callback);
}
void popup_destroy_resource_callback(struct wl_resource*) {}
-void zxdg_surface_v6_get_popup(wl_client* client,
- wl_resource* surface,
- uint32_t id,
- wl_resource* parent_surface,
- wl_resource* positioner) {
+void xdg_surface_get_popup(wl_client* client, wl_resource* surface, uint32_t id,
+ wl_resource* parent_surface,
+ wl_resource* positioner) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface
<< " id=" << id
@@ -292,19 +257,15 @@
<< " positioner=" << positioner;
wl_resource* xdg_popup_resource =
- wl_resource_create(client, &zxdg_popup_v6_interface, 1, id);
+ wl_resource_create(client, &xdg_popup_interface, 1, id);
- wl_resource_set_implementation(xdg_popup_resource,
- &zxdg_popup_v6_implementation, nullptr,
- popup_destroy_resource_callback);
+ wl_resource_set_implementation(xdg_popup_resource, &xdg_popup_implementation,
+ nullptr, popup_destroy_resource_callback);
}
-void zxdg_surface_v6_set_window_geometry(wl_client*,
- wl_resource* surface,
- int32_t x,
- int32_t y,
- int32_t w,
- int32_t h) {
+void xdg_surface_set_window_geometry(wl_client*, wl_resource* surface,
+ int32_t x, int32_t y, int32_t w,
+ int32_t h) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface
<< " x=" << x
@@ -313,23 +274,21 @@
<< " h=" << h;
}
-void zxdg_surface_v6_ack_configure(wl_client*,
- wl_resource* surface,
- uint32_t serial) {
+void xdg_surface_ack_configure(wl_client*, wl_resource* surface,
+ uint32_t serial) {
LOG(VERBOSE) << __FUNCTION__
<< " surface=" << surface
<< " serial=" << serial;
}
-const struct zxdg_surface_v6_interface zxdg_surface_v6_implementation = {
- .destroy = zxdg_surface_v6_destroy,
- .get_toplevel = zxdg_surface_v6_get_toplevel,
- .get_popup = zxdg_surface_v6_get_popup,
- .set_window_geometry = zxdg_surface_v6_set_window_geometry,
- .ack_configure = zxdg_surface_v6_ack_configure
-};
+const struct xdg_surface_interface xdg_surface_implementation = {
+ .destroy = xdg_surface_destroy,
+ .get_toplevel = xdg_surface_get_toplevel,
+ .get_popup = xdg_surface_get_popup,
+ .set_window_geometry = xdg_surface_set_window_geometry,
+ .ack_configure = xdg_surface_ack_configure};
-void zxdg_shell_v6_destroy(wl_client*, wl_resource* shell) {
+void xdg_shell_destroy(wl_client*, wl_resource* shell) {
LOG(VERBOSE) << __FUNCTION__
<< " shell=" << shell;
@@ -338,65 +297,60 @@
void positioner_destroy_resource_callback(struct wl_resource*) {}
-void zxdg_shell_v6_create_positioner(wl_client* client,
- wl_resource* shell,
- uint32_t id) {
+void xdg_shell_create_positioner(wl_client* client, wl_resource* shell,
+ uint32_t id) {
LOG(VERBOSE) << __FUNCTION__
<< " shell=" << shell
<< " id=" << id;
wl_resource* positioner_resource =
- wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id);
+ wl_resource_create(client, &xdg_positioner_interface, 1, id);
wl_resource_set_implementation(positioner_resource,
- &zxdg_positioner_v6_implementation, nullptr,
+ &xdg_positioner_implementation, nullptr,
positioner_destroy_resource_callback);
}
void surface_destroy_resource_callback(struct wl_resource*) {}
-void zxdg_shell_v6_get_xdg_surface(wl_client* client,
- wl_resource* shell,
- uint32_t id,
- wl_resource* surface) {
+void xdg_shell_get_xdg_surface(wl_client* client, wl_resource* shell,
+ uint32_t id, wl_resource* surface) {
LOG(VERBOSE) << __FUNCTION__
<< " shell=" << shell
<< " id=" << id
<< " surface=" << surface;
wl_resource* surface_resource =
- wl_resource_create(client, &zxdg_surface_v6_interface, 1, id);
+ wl_resource_create(client, &xdg_surface_interface, 1, id);
- wl_resource_set_implementation(surface_resource,
- &zxdg_surface_v6_implementation, nullptr,
- surface_destroy_resource_callback);
+ wl_resource_set_implementation(surface_resource, &xdg_surface_implementation,
+ nullptr, surface_destroy_resource_callback);
}
-void zxdg_shell_v6_pong(wl_client*, wl_resource* shell, uint32_t serial) {
+void xdg_shell_pong(wl_client*, wl_resource* shell, uint32_t serial) {
LOG(VERBOSE) << __FUNCTION__
<< " shell=" << shell
<< " serial=" << serial;
}
-const struct zxdg_shell_v6_interface zxdg_shell_v6_implementation = {
- .destroy = zxdg_shell_v6_destroy,
- .create_positioner = zxdg_shell_v6_create_positioner,
- .get_xdg_surface = zxdg_shell_v6_get_xdg_surface,
- .pong = zxdg_shell_v6_pong
-};
+const struct xdg_wm_base_interface xdg_shell_implementation = {
+ .destroy = xdg_shell_destroy,
+ .create_positioner = xdg_shell_create_positioner,
+ .get_xdg_surface = xdg_shell_get_xdg_surface,
+ .pong = xdg_shell_pong};
void bind_shell(wl_client* client, void* data, uint32_t version, uint32_t id) {
wl_resource* shell_resource =
- wl_resource_create(client, &zxdg_shell_v6_interface, version, id);
+ wl_resource_create(client, &xdg_wm_base_interface, version, id);
- wl_resource_set_implementation(shell_resource,
- &zxdg_shell_v6_implementation, data, nullptr);
+ wl_resource_set_implementation(shell_resource, &xdg_shell_implementation,
+ data, nullptr);
}
} // namespace
void BindShellInterface(wl_display* display) {
- wl_global_create(display, &zxdg_shell_v6_interface, 1, nullptr, bind_shell);
+ wl_global_create(display, &xdg_wm_base_interface, 1, nullptr, bind_shell);
}
} // namespace wayland
\ No newline at end of file
diff --git a/host/libs/wayland/wayland_surface.cpp b/host/libs/wayland/wayland_surface.cpp
index e9abece..51a3b42 100644
--- a/host/libs/wayland/wayland_surface.cpp
+++ b/host/libs/wayland/wayland_surface.cpp
@@ -23,8 +23,7 @@
namespace wayland {
-Surface::Surface(std::uint32_t display_number, Surfaces& surfaces)
- : display_number_(display_number), surfaces_(surfaces) {}
+Surface::Surface(Surfaces& surfaces) : surfaces_(surfaces) {}
void Surface::SetRegion(const Region& region) {
std::unique_lock<std::mutex> lock(state_mutex_);
@@ -45,22 +44,28 @@
return;
}
- struct wl_shm_buffer* shm_buffer = wl_shm_buffer_get(state_.current_buffer);
- CHECK(shm_buffer != nullptr);
+ if (state_.virtio_gpu_metadata_.scanout_id.has_value()) {
+ const uint32_t display_number = *state_.virtio_gpu_metadata_.scanout_id;
- wl_shm_buffer_begin_access(shm_buffer);
+ struct wl_shm_buffer* shm_buffer = wl_shm_buffer_get(state_.current_buffer);
+ CHECK(shm_buffer != nullptr);
- const int32_t buffer_w = wl_shm_buffer_get_width(shm_buffer);
- CHECK(buffer_w == state_.region.w);
- const int32_t buffer_h = wl_shm_buffer_get_height(shm_buffer);
- CHECK(buffer_h == state_.region.h);
+ wl_shm_buffer_begin_access(shm_buffer);
- uint8_t* buffer_pixels =
- reinterpret_cast<uint8_t*>(wl_shm_buffer_get_data(shm_buffer));
+ const int32_t buffer_w = wl_shm_buffer_get_width(shm_buffer);
+ CHECK(buffer_w == state_.region.w);
+ const int32_t buffer_h = wl_shm_buffer_get_height(shm_buffer);
+ CHECK(buffer_h == state_.region.h);
+ const int32_t buffer_stride_bytes = wl_shm_buffer_get_stride(shm_buffer);
- surfaces_.HandleSurfaceFrame(display_number_, buffer_pixels);
+ uint8_t* buffer_pixels =
+ reinterpret_cast<uint8_t*>(wl_shm_buffer_get_data(shm_buffer));
- wl_shm_buffer_end_access(shm_buffer);
+ surfaces_.HandleSurfaceFrame(display_number, buffer_w, buffer_h,
+ buffer_stride_bytes, buffer_pixels);
+
+ wl_shm_buffer_end_access(shm_buffer);
+ }
wl_buffer_send_release(state_.current_buffer);
wl_client_flush(wl_resource_get_client(state_.current_buffer));
@@ -69,4 +74,9 @@
state_.current_frame_number++;
}
+void Surface::SetVirtioGpuScanoutId(uint32_t scanout_id) {
+ std::unique_lock<std::mutex> lock(state_mutex_);
+ state_.virtio_gpu_metadata_.scanout_id = scanout_id;
+}
+
} // namespace wayland
\ No newline at end of file
diff --git a/host/libs/wayland/wayland_surface.h b/host/libs/wayland/wayland_surface.h
index fcf2345..f3b17ae 100644
--- a/host/libs/wayland/wayland_surface.h
+++ b/host/libs/wayland/wayland_surface.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <mutex>
+#include <optional>
#include <wayland-server-core.h>
@@ -29,7 +30,7 @@
// Tracks the buffer associated with a Wayland surface.
class Surface {
public:
- Surface(std::uint32_t display_number, Surfaces& surfaces);
+ Surface(Surfaces& surfaces);
virtual ~Surface() = default;
Surface(const Surface& rhs) = delete;
@@ -53,10 +54,15 @@
// Commits the pending frame state.
void Commit();
+ void SetVirtioGpuScanoutId(uint32_t scanout);
+
private:
- std::uint32_t display_number_;
Surfaces& surfaces_;
+ struct VirtioGpuMetadata {
+ std::optional<uint32_t> scanout_id;
+ };
+
struct State {
uint32_t current_frame_number = 0;
@@ -68,6 +74,8 @@
// The buffers expected dimensions.
Region region;
+
+ VirtioGpuMetadata virtio_gpu_metadata_;
};
std::mutex state_mutex_;
diff --git a/host/libs/wayland/wayland_surfaces.cpp b/host/libs/wayland/wayland_surfaces.cpp
index 7096574..2fa1ed9 100644
--- a/host/libs/wayland/wayland_surfaces.cpp
+++ b/host/libs/wayland/wayland_surfaces.cpp
@@ -23,42 +23,20 @@
namespace wayland {
-Surface* Surfaces::GetOrCreateSurface(std::uint32_t id) {
- std::unique_lock<std::mutex> lock(surfaces_mutex_);
-
- auto [it, inserted] = surfaces_.try_emplace(id, nullptr);
-
- std::unique_ptr<Surface>& surface_ptr = it->second;
- if (inserted) {
- surface_ptr.reset(new Surface(id, *this));
- }
- return surface_ptr.get();
-}
-
-void Surfaces::OnNextFrame(const FrameCallback& frame_callback) {
- // Wraps the given callback in a std::package_task that can be waited upon
- // for completion.
- Surfaces::FrameCallbackPackaged frame_callback_packaged(
- [&frame_callback](std::uint32_t display_number,
- std::uint8_t* frame_pixels) {
- frame_callback(display_number, frame_pixels);
- });
-
- {
- std::unique_lock<std::mutex> lock(callback_mutex_);
- callback_.emplace(&frame_callback_packaged);
- }
-
- // Blocks until the frame_callback_packaged was called.
- frame_callback_packaged.get_future().get();
+void Surfaces::SetFrameCallback(FrameCallback callback) {
+ std::unique_lock<std::mutex> lock(callback_mutex_);
+ callback_.emplace(std::move(callback));
}
void Surfaces::HandleSurfaceFrame(std::uint32_t display_number,
+ std::uint32_t frame_width,
+ std::uint32_t frame_height,
+ std::uint32_t frame_stride_bytes,
std::uint8_t* frame_bytes) {
std::unique_lock<std::mutex> lock(callback_mutex_);
if (callback_) {
- (*callback_.value())(display_number, frame_bytes);
- callback_.reset();
+ (callback_.value())(display_number, frame_width, frame_height,
+ frame_stride_bytes, frame_bytes);
}
}
diff --git a/host/libs/wayland/wayland_surfaces.h b/host/libs/wayland/wayland_surfaces.h
index ec67ca4..58ed0e8 100644
--- a/host/libs/wayland/wayland_surfaces.h
+++ b/host/libs/wayland/wayland_surfaces.h
@@ -39,27 +39,25 @@
Surfaces(Surfaces&& rhs) = delete;
Surfaces& operator=(Surfaces&& rhs) = delete;
- Surface* GetOrCreateSurface(std::uint32_t id);
+ using FrameCallback =
+ std::function<void(std::uint32_t /*display_number*/, //
+ std::uint32_t /*frame_width*/, //
+ std::uint32_t /*frame_height*/, //
+ std::uint32_t /*frame_stride_bytes*/, //
+ std::uint8_t* /*frame_bytes*/)>;
- using FrameCallback = std::function<void(std::uint32_t /*display_number*/,
- std::uint8_t* /*frame_pixels*/)>;
-
- // Blocking
- void OnNextFrame(const FrameCallback& callback);
+ void SetFrameCallback(FrameCallback callback);
private:
friend class Surface;
- void HandleSurfaceFrame(std::uint32_t display_number,
+ void HandleSurfaceFrame(std::uint32_t display_number, //
+ std::uint32_t frame_width, //
+ std::uint32_t frame_height, //
+ std::uint32_t frame_stride_bytes, //
std::uint8_t* frame_bytes);
- std::mutex surfaces_mutex_;
- std::unordered_map<std::uint32_t, std::unique_ptr<Surface>> surfaces_;
-
- using FrameCallbackPackaged = std::packaged_task<void(
- std::uint32_t /*display_number*/, std::uint8_t* /*frame_bytes*/)>;
-
std::mutex callback_mutex_;
- std::optional<FrameCallbackPackaged*> callback_;
+ std::optional<FrameCallback> callback_;
};
} // namespace wayland
diff --git a/host/libs/wayland/wayland_virtio_gpu_metadata.cpp b/host/libs/wayland/wayland_virtio_gpu_metadata.cpp
new file mode 100644
index 0000000..e26c653
--- /dev/null
+++ b/host/libs/wayland/wayland_virtio_gpu_metadata.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/libs/wayland/wayland_compositor.h"
+
+#include <android-base/logging.h>
+
+#include <virtio-gpu-metadata-v1.h>
+#include <wayland-server-core.h>
+#include <wayland-server-protocol.h>
+
+#include "host/libs/wayland/wayland_surface.h"
+#include "host/libs/wayland/wayland_utils.h"
+
+namespace wayland {
+namespace {
+
+void virtio_gpu_surface_metadata_set_scanout_id(
+ struct wl_client*, struct wl_resource* surface_metadata_resource,
+ uint32_t scanout_id) {
+ GetUserData<Surface>(surface_metadata_resource)
+ ->SetVirtioGpuScanoutId(scanout_id);
+}
+
+const struct wp_virtio_gpu_surface_metadata_v1_interface
+ virtio_gpu_surface_metadata_implementation = {
+ .set_scanout_id = virtio_gpu_surface_metadata_set_scanout_id};
+
+void destroy_virtio_gpu_surface_metadata_resource_callback(
+ struct wl_resource*) {
+ // This is only expected to occur upon surface destruction so no need to
+ // update the scanout id in `Surface`.
+}
+
+void virtio_gpu_metadata_get_surface_metadata(
+ struct wl_client* client, struct wl_resource* /*metadata_impl_resource*/,
+ uint32_t id, struct wl_resource* surface_resource) {
+ Surface* surface = GetUserData<Surface>(surface_resource);
+
+ wl_resource* virtio_gpu_metadata_surface_resource = wl_resource_create(
+ client, &wp_virtio_gpu_surface_metadata_v1_interface, 1, id);
+
+ wl_resource_set_implementation(
+ virtio_gpu_metadata_surface_resource,
+ &virtio_gpu_surface_metadata_implementation, surface,
+ destroy_virtio_gpu_surface_metadata_resource_callback);
+}
+
+const struct wp_virtio_gpu_metadata_v1_interface
+ virtio_gpu_metadata_implementation = {
+ .get_surface_metadata = virtio_gpu_metadata_get_surface_metadata,
+};
+
+void destroy_virtio_gpu_metadata_resource_callback(struct wl_resource*) {}
+
+void bind_virtio_gpu_metadata(wl_client* client, void* data,
+ uint32_t /*version*/, uint32_t id) {
+ wl_resource* resource =
+ wl_resource_create(client, &wp_virtio_gpu_metadata_v1_interface, 1, id);
+
+ wl_resource_set_implementation(resource, &virtio_gpu_metadata_implementation,
+ data,
+ destroy_virtio_gpu_metadata_resource_callback);
+}
+
+} // namespace
+
+void BindVirtioGpuMetadataInterface(wl_display* display, Surfaces* surfaces) {
+ wl_global_create(display, &wp_virtio_gpu_metadata_v1_interface, 1, surfaces,
+ bind_virtio_gpu_metadata);
+}
+
+} // namespace wayland
\ No newline at end of file
diff --git a/host/libs/wayland/wayland_virtio_gpu_metadata.h b/host/libs/wayland/wayland_virtio_gpu_metadata.h
new file mode 100644
index 0000000..c414c84
--- /dev/null
+++ b/host/libs/wayland/wayland_virtio_gpu_metadata.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <wayland-server-core.h>
+
+#include "host/libs/wayland/wayland_surfaces.h"
+
+namespace wayland {
+
+// Binds the virtio gpu metadata interface to the given wayland server.
+void BindVirtioGpuMetadataInterface(wl_display* display, Surfaces* surfaces);
+
+} // namespace wayland
\ No newline at end of file
diff --git a/host/libs/websocket/Android.bp b/host/libs/websocket/Android.bp
index 2a4a522..327224b 100644
--- a/host/libs/websocket/Android.bp
+++ b/host/libs/websocket/Android.bp
@@ -28,6 +28,7 @@
"liblog",
"libssl",
"libcrypto",
+ "libcuttlefish_utils",
],
static_libs: [
"libcap",
diff --git a/host/libs/websocket/websocket_server.cpp b/host/libs/websocket/websocket_server.cpp
index cdfcba2..40f1357 100644
--- a/host/libs/websocket/websocket_server.cpp
+++ b/host/libs/websocket/websocket_server.cpp
@@ -22,6 +22,7 @@
#include <android-base/logging.h>
#include <libwebsockets.h>
+#include <common/libs/utils/files.h>
#include <host/libs/websocket/websocket_handler.h>
namespace cuttlefish {
@@ -32,6 +33,7 @@
int server_port) {
std::string cert_file = certs_dir + "/server.crt";
std::string key_file = certs_dir + "/server.key";
+ std::string ca_file = certs_dir + "/CA.crt";
retry_ = {
.secs_since_valid_ping = 3,
@@ -63,11 +65,10 @@
};
struct lws_context_creation_info info;
- headers_ = {NULL, NULL,
- "content-security-policy:",
- "default-src 'self'; "
- "style-src 'self' https://fonts.googleapis.com/; "
- "font-src https://fonts.gstatic.com/; "};
+ headers_ = {NULL, NULL, "content-security-policy:",
+ "default-src 'self' https://ajax.googleapis.com; "
+ "style-src 'self' https://fonts.googleapis.com/; "
+ "font-src https://fonts.gstatic.com/; "};
memset(&info, 0, sizeof info);
info.port = server_port;
@@ -79,6 +80,9 @@
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.ssl_cert_filepath = cert_file.c_str();
info.ssl_private_key_filepath = key_file.c_str();
+ if (FileExists(ca_file)) {
+ info.ssl_ca_filepath = ca_file.c_str();
+ }
info.retry_and_idle_policy = &retry_;
context_ = lws_create_context(&info);
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index 5f118b0..6be1730 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -114,9 +114,9 @@
BOARD_MALLOC_ALIGNMENT := 16
# Disable sparse on all filesystem images
-TARGET_USERIMAGES_SPARSE_EROFS_DISABLED := true
-TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true
-TARGET_USERIMAGES_SPARSE_F2FS_DISABLED := true
+TARGET_USERIMAGES_SPARSE_EROFS_DISABLED ?= true
+TARGET_USERIMAGES_SPARSE_EXT_DISABLED ?= true
+TARGET_USERIMAGES_SPARSE_F2FS_DISABLED ?= true
# Make the userdata partition 6G to accommodate ASAN and CTS
BOARD_USERDATAIMAGE_PARTITION_SIZE := $(TARGET_USERDATAIMAGE_PARTITION_SIZE)
@@ -159,13 +159,6 @@
BOARD_SEPOLICY_DIRS += system/bt/vendor_libs/linux/sepolicy
-# Avoid multiple includes of sepolicy already included by Pixel experience.
-ifneq ($(filter aosp_% %_auto %_go_phone trout_% %_tv,$(PRODUCT_NAME)),)
-
-SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS += hardware/google/pixel-sepolicy/flipendo
-
-endif
-
# 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
@@ -220,8 +213,6 @@
# TODO(b/182417593): Move all of these module options to modules.options
# TODO(b/176860479): Remove once goldfish and cuttlefish share a wifi implementation
BOARD_BOOTCONFIG += kernel.mac80211_hwsim.radios=0
-# TODO(b/175151042): Remove once we are using virtio-snd on cuttlefish
-BOARD_BOOTCONFIG += kernel.snd-hda-intel.enable=0
# Reduce slab size usage from virtio vsock to reduce slab fragmentation
BOARD_BOOTCONFIG += \
kernel.vmw_vsock_virtio_transport_common.virtio_transport_max_vsock_pkt_buf_size=16384
diff --git a/shared/auto/audio_policy_configuration.xml b/shared/auto/audio_policy_configuration.xml
new file mode 100644
index 0000000..93d4130
--- /dev/null
+++ b/shared/auto/audio_policy_configuration.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!--
+ Overlay resources to configure car service based on each OEM's preference.
+ See also packages/services/Car/service/res/values/config.xml
+-->
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!-- Global configuration Decalaration -->
+ <globalConfiguration speaker_drc_enabled="true"/>
+
+ <module name="primary" halVersion="2.0">
+ <attachedDevices>
+ <item>Speaker</item>
+ <item>Built-In Mic</item>
+ </attachedDevices>
+ <defaultOutputDevice>Speaker</defaultOutputDevice>
+ <mixPorts>
+ <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="primary input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="Speaker">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+ </gains>
+ </devicePort>
+
+ <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+ </devicePort>
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Speaker"
+ sources="primary output"/>
+ <route type="mix" sink="primary input"
+ sources="Built-In Mic"/>
+ </routes>
+ </module>
+
+ <xi:include href="audio_policy_volumes.xml"/>
+ <xi:include href="default_volume_tables.xml"/>
+
+ <!-- End of Volume section -->
+ <!-- End of Modules section -->
+
+</audioPolicyConfiguration>
\ No newline at end of file
diff --git a/shared/auto/car_audio_configuration.xml b/shared/auto/car_audio_configuration.xml
new file mode 100644
index 0000000..53ca217
--- /dev/null
+++ b/shared/auto/car_audio_configuration.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ Defines the audio configuration in a car, including
+ - Audio zones
+ - Context to audio bus mappings
+ - Volume groups
+ in the car environment.
+-->
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true">
+ <volumeGroups>
+ <group>
+ <device address="Speaker">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ <context context="emergency"/>
+ <context context="safety"/>
+ <context context="vehicle_status"/>
+ <context context="announcement"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/shared/auto/device.mk b/shared/auto/device.mk
index fb1ccdf..3e6604e 100644
--- a/shared/auto/device.mk
+++ b/shared/auto/device.mk
@@ -45,6 +45,16 @@
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 \
+# Preinstalled packages per user type
+PRODUCT_COPY_FILES += \
+ device/google/cuttlefish/shared/auto/preinstalled-packages-product-car-cuttlefish.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/sysconfig/preinstalled-packages-product-car-cuttlefish.xml
+
+LOCAL_AUDIO_PRODUCT_COPY_FILES ?= \
+ device/google/cuttlefish/shared/auto/car_audio_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/car_audio_configuration.xml \
+ device/google/cuttlefish/shared/auto/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
+ frameworks/av/services/audiopolicy/config/a2dp_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/a2dp_audio_policy_configuration.xml \
+ frameworks/av/services/audiopolicy/config/usb_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/usb_audio_policy_configuration.xml \
+
PRODUCT_VENDOR_PROPERTIES += \
keyguard.no_require_sim=true \
ro.cdma.home.operator.alpha=Android \
@@ -73,9 +83,13 @@
canhaldump \
canhalsend
-PRODUCT_PACKAGES += \
- libcuttlefish-ril-2 \
- libcuttlefish-rild
+# Cuttlefish RIL support
+TARGET_USES_CF_RILD ?= false
+ifeq ($(TARGET_USES_CF_RILD),true)
+ PRODUCT_PACKAGES += \
+ libcuttlefish-ril-2 \
+ libcuttlefish-rild
+endif
# system_other support
PRODUCT_PACKAGES += \
diff --git a/shared/auto/manifest.xml b/shared/auto/manifest.xml
index 7bf5d3c..e1691a1 100644
--- a/shared/auto/manifest.xml
+++ b/shared/auto/manifest.xml
@@ -10,39 +10,9 @@
-->
<!-- Android Auto Embedded specific HALs-->
<manifest version="1.0" type="device">
- <!-- FIXME: Implement automotive.evs HAL
- <hal format="hidl">
- <name>android.hardware.automotive.evs</name>
- <transport>hwbinder</transport>
- <version>1.0</version>
- <interface>
- <name>IEvsEnumerator</name>
- <instance>default</instance>
- </interface>
- </hal>
- -->
<hal format="hidl">
<name>android.hardware.automotive.can</name>
<transport>hwbinder</transport>
- <version>1.0</version>
- <interface>
- <name>ICanController</name>
- <instance>socketcan</instance>
- </interface>
- <interface>
- <name>ICanBus</name>
- <instance>test</instance>
- </interface>
- </hal>
- <!-- FIXME: Move this to shared manifest.xml -->
- <hal format="hidl">
- <name>android.hardware.broadcastradio</name>
- <transport>hwbinder</transport>
- <version>2.0</version>
- <interface>
- <name>IBroadcastRadio</name>
- <instance>amfm</instance>
- <instance>dab</instance>
- </interface>
+ <fqname>@1.0::ICanBus/test</fqname>
</hal>
</manifest>
diff --git a/shared/auto/overlay/frameworks/base/core/res/res/values/config.xml b/shared/auto/overlay/frameworks/base/core/res/res/values/config.xml
index 60c7ae9..e42a10f 100644
--- a/shared/auto/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/shared/auto/overlay/frameworks/base/core/res/res/values/config.xml
@@ -16,6 +16,11 @@
** limitations under the License.
*/
-->
+
+<!--
+ Overlay resources to configure car service based on each OEM's preference.
+ See also packages/services/Car/service/res/values/config.xml
+-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Enable multi-user. -->
<bool name="config_enableMultiUserUI" translatable="false">true</bool>
@@ -23,8 +28,14 @@
<bool name="config_guestUserEphemeral" translatable="false">true</bool>
<!-- Maximum number of users allowed on the device. -->
<integer name="config_multiuserMaximumUsers" translatable="false">4</integer>
- <!-- Restricting eth2 -->
+ <!-- Restricting eth1 -->
<string-array translatable="false" name="config_ethernet_interfaces">
- <item>eth2;11,12,14;;</item>
+ <item>eth1;11,12,14;;</item>
</string-array>
+ <!-- Car uses hardware amplifier for volume. -->
+ <bool name="config_useFixedVolume">true</bool>
+ <!--
+ Handle volume keys directly in CarAudioService without passing them to the foreground app
+ -->
+ <bool name="config_handleVolumeKeysInWindowManager">true</bool>
</resources>
diff --git a/shared/auto/overlay/packages/services/Car/service/res/values/config.xml b/shared/auto/overlay/packages/services/Car/service/res/values/config.xml
new file mode 100644
index 0000000..6cfd2cc
--- /dev/null
+++ b/shared/auto/overlay/packages/services/Car/service/res/values/config.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!--
+ Overlay resources to configure car service based on each OEM's preference.
+ See also packages/services/Car/service/res/values/config.xml
+-->
+<resources>
+ <bool name="audioUseDynamicRouting">true</bool>
+ <!-- Configuration to enable muting of individual volume groups. If this is set to
+ false, muting of individual volume groups is disabled, instead muting will toggle master
+ mute. If this is set to true, car volume group muting is enabled and each individual
+ volume group can be muted separately. -->
+ <bool name="audioUseCarVolumeGroupMuting">false</bool>
+ <!-- Configuration to enable IAudioControl#onDevicesToDuckChange API to inform HAL when to
+ duck. If this is set to true, the API will receive signals indicating which output devices
+ to duck as well as what usages are currently holding focus. If set to false, the API will
+ not be called. -->
+ <bool name="audioUseHalDuckingSignals">false</bool>
+
+ <!--
+ Lists all occupant (= driver + passenger) zones available in the car.
+ Some examples are:
+ <item>occupantZoneId=0,occupantType=DRIVER,seatRow=1,seatSide=driver</item>
+ <item>occupantZoneId=1,occupantType=FRONT_PASSENGER,seatRow=1,seatSide=oppositeDriver</item>
+ <item>occupantZoneId=2,occupantType=REAR_PASSENGER,seatRow=2,seatSide=left</item>
+ <item>occupantZoneId=3,occupantType=REAR_PASSENGER,seatRow=2,seatSide=right</item>
+
+ occupantZoneId: Unique unsigned integer id to represent each passenger zone. Each zone
+ should have different id.
+ occupantType: Occupant type for the display. Use * part from
+ CarOccupantZoneManager.OCCUPANT_TYPE_* like DRIVER, FRONT_PASSENGER,
+ REAR_PASSENGER and etc.
+ seatRow: Integer telling which row the seat is located. Row 1 is for front seats.
+ seatSide: left/center/right for known side. Or can use driver/center/oppositeDriver to
+ handle both right-hand driving and left-hand driving in one place.
+ If car's RHD / LHD is not specified, LHD will be assumed and driver side becomes
+ left.
+ -->
+ <string-array translatable="false" name="config_occupant_zones">
+ <item>occupantZoneId=0,occupantType=DRIVER,seatRow=1,seatSide=driver</item>
+ </string-array>
+ <!--
+ Specifies configuration of displays in system telling its usage / type and assigned
+ occupant.
+
+ Some examples are:
+ <item>displayPort=0,displayType=MAIN,occupantZoneId=0</item>
+ <item>displayPort=1,displayType=INSTRUMENT_CLUSTER,occupantZoneId=0</item>
+ <item>displayPort=2,displayType=MAIN,occupantZoneId=1</item>
+ <item>displayPort=3,displayType=MAIN,occupantZoneId=2</item>
+ <item>displayPort=4,displayType=MAIN,occupantZoneId=3</item>
+
+ displayPort: Unique port id for the display.
+ displayType: Display type for the display. Use * part from
+ CarOccupantZoneManager.DISPLAY_TYPE_* like MAIN, INSTRUMENT_CLUSTER and
+ etc.
+ occupantZoneId: occupantZoneId specified from config_occupant_zones.
+
+ -->
+ <string-array translatable="false" name="config_occupant_display_mapping">
+ <item>displayPort=0,displayType=MAIN,occupantZoneId=0</item>
+ <item>displayPort=1,displayType=INSTRUMENT_CLUSTER,occupantZoneId=0</item>
+ </string-array>
+
+ <!-- A name of a camera device that provides the rearview through EVS service -->
+ <string translatable="false" name="config_evsRearviewCameraId">/dev/video10</string>
+
+ <!-- The camera Activity name for EVS, if defined, the Activity will be launched by
+ CarEvsService. -->
+ <string name="config_evsCameraActivity" translatable="false">
+ com.google.android.car.evs/com.google.android.car.evs.CarEvsCameraPreviewActivity
+ </string>
+</resources>
diff --git a/vsoc_x86/auto/preinstalled-packages-product-car-cuttlefish.xml b/shared/auto/preinstalled-packages-product-car-cuttlefish.xml
similarity index 96%
rename from vsoc_x86/auto/preinstalled-packages-product-car-cuttlefish.xml
rename to shared/auto/preinstalled-packages-product-car-cuttlefish.xml
index 49aef5d..a156ae8 100644
--- a/vsoc_x86/auto/preinstalled-packages-product-car-cuttlefish.xml
+++ b/shared/auto/preinstalled-packages-product-car-cuttlefish.xml
@@ -30,10 +30,6 @@
<install-in user-type="FULL" />
<install-in user-type="SYSTEM" />
</install-in-user-type>
- <install-in-user-type package="com.android.managedprovisioning">
- <install-in user-type="FULL" />
- <install-in user-type="SYSTEM" />
- </install-in-user-type>
<install-in-user-type package="com.android.phone">
<install-in user-type="FULL" />
<install-in user-type="SYSTEM" />
@@ -94,8 +90,13 @@
<install-in-user-type package="com.android.cameraextensions">
<install-in user-type="SYSTEM" />
<install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.car.messenger">
+ <install-in user-type="FULL" />
+ <install-in user-type="SYSTEM" />
</install-in-user-type>
+
<!--
Apps that do need to run on SYSTEM and evaluated by package owner.
Here the apps will have FULL only.
@@ -136,9 +137,6 @@
<install-in-user-type package="com.android.car.radio">
<install-in user-type="FULL" />
</install-in-user-type>
- <install-in-user-type package="com.android.car.messenger">
- <install-in user-type="FULL" />
- </install-in-user-type>
<install-in-user-type package="com.android.car.media.localmediaplayer">
<install-in user-type="FULL" />
</install-in-user-type>
diff --git a/shared/config/config_foldable.json b/shared/config/config_foldable.json
index dd64d17..a9c7255 100644
--- a/shared/config/config_foldable.json
+++ b/shared/config/config_foldable.json
@@ -1,7 +1,6 @@
{
- "x_res" : 1768,
- "y_res" : 2208,
- "dpi" : 386,
+ "display0" : "width=1768,height=2208,dpi=374",
+ "display1" : "width=832,height=2268,dpi=387",
"memory_mb" : 4096,
"custom_actions" : [
{
@@ -21,6 +20,19 @@
"device_states": [
{
"lid_switch_open": true,
+ "hinge_angle_value": 90
+ }
+ ],
+ "button":{
+ "command":"device_state_half_opened",
+ "title":"Device State Half-Opened",
+ "icon_name":"laptop"
+ }
+ },
+ {
+ "device_states": [
+ {
+ "lid_switch_open": true,
"hinge_angle_value": 180
}
],
diff --git a/shared/config/init.vendor.rc b/shared/config/init.vendor.rc
index 77b79dc..94ef593 100644
--- a/shared/config/init.vendor.rc
+++ b/shared/config/init.vendor.rc
@@ -49,9 +49,7 @@
start setup_wifi
# works around framework netiface enumeration issue
- start rename_eth1
-
- start bt_vhci_forwarder
+ start rename_eth0
# So GceBootReporter can print to kmsg
chmod 622 /dev/kmsg
@@ -60,6 +58,8 @@
# set RLIMIT_MEMLOCK to 64MB
setrlimit 8 67108864 67108864
+ start bt_vhci_forwarder
+
on post-fs-data
mkdir /data/vendor/modem_dump 0777 system system
mkdir /data/vendor/radio 0777 system system
@@ -79,15 +79,16 @@
mkdir /data/vendor/wifi/wpa 0770 wifi wifi
mkdir /data/vendor/wifi/wpa/sockets 0770 wifi wifi
start socket_vsock_proxy
+ setprop ro.hardware.audio.primary goldfish
-service bt_vhci_forwarder /vendor/bin/bt_vhci_forwarder -virtio_console_dev=/dev/hvc5
+service bt_vhci_forwarder /vendor/bin/bt_vhci_forwarder -virtio_console_dev=${vendor.ser.bt-uart}
user bluetooth
group bluetooth
service setup_wifi /vendor/bin/setup_wifi
oneshot
-service rename_eth1 /vendor/bin/rename_netiface eth1 rmnet0
+service rename_eth0 /vendor/bin/rename_netiface eth0 rmnet0
oneshot
on property:sys.boot_completed=1
diff --git a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_0.idc
similarity index 64%
copy from shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc
copy to shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_0.idc
index 662f467..a4e2b19 100644
--- a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc
+++ b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_0.idc
@@ -3,4 +3,4 @@
touch.deviceType = touchScreen
touch.orientationAware = 1
-# touch.displayId = local:0
+touch.displayId = local:4619827259835644672
diff --git a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_1.idc
similarity index 64%
copy from shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc
copy to shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_1.idc
index 662f467..a375e58 100644
--- a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc
+++ b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_1.idc
@@ -3,4 +3,4 @@
touch.deviceType = touchScreen
touch.orientationAware = 1
-# touch.displayId = local:0
+touch.displayId = local:4619827551948147201
diff --git a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_2.idc
similarity index 64%
rename from shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc
rename to shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_2.idc
index 662f467..db17d3c 100644
--- a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc
+++ b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_2.idc
@@ -3,4 +3,4 @@
touch.deviceType = touchScreen
touch.orientationAware = 1
-# touch.displayId = local:0
+touch.displayId = local:4619827124781842690
diff --git a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_3.idc
similarity index 64%
copy from shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc
copy to shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_3.idc
index 662f467..b910ad3 100644
--- a/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc
+++ b/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_3.idc
@@ -3,4 +3,4 @@
touch.deviceType = touchScreen
touch.orientationAware = 1
-# touch.displayId = local:0
+touch.displayId = local:4619827540095559171
diff --git a/shared/config/manifest.xml b/shared/config/manifest.xml
index baca8df..5917647 100644
--- a/shared/config/manifest.xml
+++ b/shared/config/manifest.xml
@@ -18,18 +18,9 @@
-->
<manifest version="1.0" type="device" target-level="6">
<hal format="hidl">
- <name>android.hardware.audio</name>
- <transport>hwbinder</transport>
- <version>6.0</version>
- <interface>
- <name>IDevicesFactory</name>
- <instance>default</instance>
- </interface>
- </hal>
- <hal format="hidl">
<name>android.hardware.audio.effect</name>
<transport>hwbinder</transport>
- <version>6.0</version>
+ <version>7.0</version>
<interface>
<name>IEffectsFactory</name>
<instance>default</instance>
@@ -97,15 +88,6 @@
</interface>
</hal>
-->
- <hal format="hidl">
- <name>android.hardware.graphics.composer</name>
- <transport>hwbinder</transport>
- <version>2.3</version>
- <interface>
- <name>IComposer</name>
- <instance>default</instance>
- </interface>
- </hal>
<!-- TODO (b/130075874):
<hal format="hidl">
<name>android.hardware.ir</name>
@@ -150,31 +132,6 @@
</interface>
</hal>
-->
- <hal format="hidl">
- <name>android.hardware.radio</name>
- <transport>hwbinder</transport>
- <version>1.6</version>
- <interface>
- <name>IRadio</name>
- <instance>slot1</instance>
- <!-- cuttlefish doesn't support SIM slot 2/3 -->
- </interface>
- <!-- TODO (b/130079344):
- <interface>
- <name>ISap</name>
- <instance>slot1</instance>
- </interface>
- -->
- </hal>
- <hal format="hidl">
- <name>android.hardware.radio.config</name>
- <transport>hwbinder</transport>
- <version>1.3</version>
- <interface>
- <name>IRadioConfig</name>
- <instance>default</instance>
- </interface>
- </hal>
<!-- TODO (b/130079239):
<hal format="hidl">
<name>android.hardware.secure_element</name>
diff --git a/shared/device.mk b/shared/device.mk
index 57dc767..ebade7a 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -27,6 +27,7 @@
$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_ramdisk.mk)
PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for vulkan
+PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for audio and wifi
PRODUCT_SHIPPING_API_LEVEL := 31
PRODUCT_USE_DYNAMIC_PARTITIONS := true
@@ -73,10 +74,6 @@
ro.com.google.locationfeatures=1 \
persist.sys.fuse.passthrough.enable=true \
-# Storage: for factory reset protection feature
-PRODUCT_VENDOR_PROPERTIES += \
- ro.frp.pst=/dev/block/by-name/frp
-
# Explanation of specific properties:
# debug.hwui.swap_with_damage avoids boot failure on M http://b/25152138
# ro.opengles.version OpenGLES 3.0
@@ -98,6 +95,12 @@
debug.c2.use_dmabufheaps=1 \
ro.camerax.extensions.enabled=true \
+LOCAL_BT_PROPERTIES ?= \
+ vendor.ser.bt-uart?=/dev/hvc5 \
+
+PRODUCT_VENDOR_PROPERTIES += \
+ ${LOCAL_BT_PROPERTIES} \
+
# Below is a list of properties we probably should get rid of.
PRODUCT_VENDOR_PROPERTIES += \
wlan.driver.status=ok
@@ -299,7 +302,10 @@
frameworks/native/data/etc/android.software.verified_boot.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.verified_boot.xml \
system/bt/vendor_libs/test_vendor_lib/data/controller_properties.json:vendor/etc/bluetooth/controller_properties.json \
device/google/cuttlefish/shared/config/task_profiles.json:$(TARGET_COPY_OUT_VENDOR)/etc/task_profiles.json \
- device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen.idc
+ device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_0.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_0.idc \
+ device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_1.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_1.idc \
+ device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_2.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_2.idc \
+ device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_3.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_3.idc
ifeq ($(TARGET_RO_FILE_SYSTEM_TYPE),ext4)
PRODUCT_COPY_FILES += \
@@ -369,8 +375,7 @@
hwcomposer.drm_minigbm \
hwcomposer.cutf \
hwcomposer-stats \
- [email protected] \
- [email protected]
+ [email protected]
#
# Gralloc HAL
@@ -403,16 +408,17 @@
# Audio HAL
#
LOCAL_AUDIO_PRODUCT_PACKAGE ?= \
- audio.primary.cutf \
- audio.r_submix.default \
- [email protected] \
- [email protected] \
- [email protected]
+ android.hardware.audio.service \
+ [email protected] \
+ [email protected] \
LOCAL_AUDIO_PRODUCT_COPY_FILES ?= \
- device/google/cuttlefish/shared/config/audio_policy.conf:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy.conf \
- frameworks/av/services/audiopolicy/config/audio_policy_configuration_generic.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
- frameworks/av/services/audiopolicy/config/primary_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/primary_audio_policy_configuration.xml
+ device/generic/goldfish/audio/policy/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
+ device/generic/goldfish/audio/policy/primary_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/primary_audio_policy_configuration.xml \
+ frameworks/av/services/audiopolicy/config/r_submix_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/r_submix_audio_policy_configuration.xml \
+ frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_volumes.xml \
+ frameworks/av/services/audiopolicy/config/default_volume_tables.xml:$(TARGET_COPY_OUT_VENDOR)/etc/default_volume_tables.xml \
+ frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \
LOCAL_AUDIO_DEVICE_PACKAGE_OVERLAYS ?=
@@ -468,11 +474,19 @@
#
# Camera
#
+ifeq ($(TARGET_USE_VSOCK_CAMERA_HAL_IMPL),true)
+PRODUCT_PACKAGES += \
+ [email protected] \
+ [email protected]
+DEVICE_MANIFEST_FILE += \
+ device/google/cuttlefish/guest/hals/camera/manifest.xml
+else
PRODUCT_PACKAGES += \
[email protected] \
libgooglecamerahwl_impl \
[email protected] \
+endif
#
# Gatekeeper
#
@@ -485,9 +499,11 @@
#
# GPS
#
-PRODUCT_PACKAGES += \
+LOCAL_GNSS_PRODUCT_PACKAGE ?= \
android.hardware.gnss-service.example
+PRODUCT_PACKAGES += $(LOCAL_GNSS_PRODUCT_PACKAGE)
+
# Health
ifeq ($(LOCAL_HEALTH_PRODUCT_PACKAGE),)
LOCAL_HEALTH_PRODUCT_PACKAGE := \
diff --git a/shared/foldable/display_layout_configuration.xml b/shared/foldable/display_layout_configuration.xml
new file mode 100644
index 0000000..54b76b1
--- /dev/null
+++ b/shared/foldable/display_layout_configuration.xml
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<layouts>
+ <layout>
+ <!-- CLOSED: display0 disabled, display1 enabled -->
+ <state>0</state>
+
+ <display enabled="false">
+ <address>4619827259835644672</address>
+ </display>
+
+ <display enabled="true" defaultDisplay="true">
+ <address>4619827551948147201</address>
+ </display>
+ </layout>
+
+ <layout>
+ <!-- HALF_OPENED: display0 enabled, display1 disabled -->
+ <state>1</state>
+
+ <display enabled="true" defaultDisplay="true">
+ <address>4619827259835644672</address>
+ </display>
+
+ <display enabled="false">
+ <address>4619827551948147201</address>
+ </display>
+ </layout>
+
+ <layout>
+ <!-- OPENED: display0 enabled, display1 disabled -->
+ <state>2</state>
+
+ <display enabled="true" defaultDisplay="true">
+ <address>4619827259835644672</address>
+ </display>
+
+ <display enabled="false">
+ <address>4619827551948147201</address>
+ </display>
+ </layout>
+</layouts>
diff --git a/shared/foldable/display_settings.xml b/shared/foldable/display_settings.xml
new file mode 100644
index 0000000..6ad8287
--- /dev/null
+++ b/shared/foldable/display_settings.xml
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<display-settings>
+ <config identifier="0" />
+ <!-- Allow rotation of fixed-orientation activities on the inner screen. -->
+ <display
+ name="local:4619827259835644672"
+ ignoreOrientationRequest="true"/>
+</display-settings>
diff --git a/shared/foldable/overlay/frameworks/base/core/res/res/values/config.xml b/shared/foldable/overlay/frameworks/base/core/res/res/values/config.xml
index 2c540e1..918d39a 100644
--- a/shared/foldable/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/shared/foldable/overlay/frameworks/base/core/res/res/values/config.xml
@@ -17,15 +17,12 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Indicate the display area rect for foldable devices in folded state. -->
- <!-- left and right bounds come from: (open_width/2) +/- (folded_width)/2 -->
- <string name="config_foldedArea">476 0 1292 2208</string>
<!-- WindowsManager JetPack display features -->
<string name="config_display_features" translatable="false">fold-[884,0,884,2208]</string>
<!-- Map of System DeviceState supplied by DeviceStateManager to WM Jetpack posture. -->
<string-array name="config_device_state_postures" translatable="false">
<item>0:1</item> <!-- CLOSED : STATE_FLAT -->
- <item>3:2</item> <!-- HALF_OPENED : STATE_HALF_OPENED -->
+ <item>1:2</item> <!-- HALF_OPENED : STATE_HALF_OPENED -->
<item>2:3</item> <!-- OPENED : STATE_FLIPPED -->
</string-array>
<!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
@@ -33,6 +30,9 @@
<integer-array name="config_foldedDeviceStates" translatable="false">
<item>0</item> <!-- CLOSED -->
</integer-array>
+ <!-- Indicates whether to enable an animation when unfolding a device or not -->
+ <bool name="config_unfoldTransitionEnabled">true</bool>
+ <bool name="config_supportsConcurrentInternalDisplays">false</bool>
<!-- Controls whether the device support multi window modes like split-screen. -->
<bool name="config_supportsMultiWindow">true</bool>
<!-- Controls whether device supports split-screen mode. -->
diff --git a/shared/pc/overlay/frameworks/base/core/res/res/values/config.xml b/shared/pc/overlay/frameworks/base/core/res/res/values/config.xml
index 86896be..313712f 100644
--- a/shared/pc/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/shared/pc/overlay/frameworks/base/core/res/res/values/config.xml
@@ -20,8 +20,8 @@
<bool name="config_showNavigationBar" translatable="false">true</bool>
<!-- Maximum number of supported users -->
<integer name="config_multiuserMaximumUsers" translatable="false">4</integer>
- <!-- Restricting eth2 -->
+ <!-- Restricting eth1 -->
<string-array translatable="false" name="config_ethernet_interfaces">
- <item>eth2;11,12,14;;</item>
+ <item>eth1;11,12,14;;</item>
</string-array>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/shared/phone/device.mk b/shared/phone/device.mk
index 4b5f041..faa2c7c 100644
--- a/shared/phone/device.mk
+++ b/shared/phone/device.mk
@@ -45,3 +45,7 @@
# These flags are important for the GSI, but break auto
# These are used by aosp_cf_x86_go_phone targets
PRODUCT_ENFORCE_RRO_TARGETS := framework-res
+
+# Storage: for factory reset protection feature
+PRODUCT_VENDOR_PROPERTIES += \
+ ro.frp.pst=/dev/block/by-name/frp
diff --git a/shared/phone/overlay/frameworks/base/core/res/res/values/config.xml b/shared/phone/overlay/frameworks/base/core/res/res/values/config.xml
index c1b0d3e..61feabf 100644
--- a/shared/phone/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/shared/phone/overlay/frameworks/base/core/res/res/values/config.xml
@@ -55,9 +55,9 @@
<string name="config_mms_user_agent_profile_url" translatable="false">http://gsm.lge.com/html/gsm/Nexus5-M3.xml</string>
<string name="config_wlan_data_service_package" translatable="false">com.android.ims</string>
<string name="config_wlan_network_service_package" translatable="false">com.android.ims</string>
- <!-- Restricting eth2 -->
+ <!-- Restricting eth1 -->
<string-array translatable="false" name="config_ethernet_interfaces">
- <item>eth2;11,12,14;;</item>
+ <item>eth1;11,12,14;;</item>
</string-array>
<!-- List of biometric sensors on the device, in decreasing strength. Consumed by AuthService
diff --git a/shared/sepolicy/system_ext/private/flipendo.te b/shared/sepolicy/system_ext/private/flipendo.te
deleted file mode 100644
index bdf57dc..0000000
--- a/shared/sepolicy/system_ext/private/flipendo.te
+++ /dev/null
@@ -1 +0,0 @@
-gpu_access(flipendo)
diff --git a/shared/sepolicy/vendor/adbd.te b/shared/sepolicy/vendor/adbd.te
index 4ed653a..d932066 100644
--- a/shared/sepolicy/vendor/adbd.te
+++ b/shared/sepolicy/vendor/adbd.te
@@ -1,9 +1,2 @@
allow adbd self:{ socket vsock_socket } {create listen accept rw_socket_perms_no_ioctl};
-# TODO(b/130668487): Label the vsock sockets.
-allow adbd unlabeled:{socket vsock_socket} rw_socket_perms_no_ioctl;
allow adbd kernel:system module_request;
-
-recovery_only(`
-# TODO(b/130668487): Label the vsock sockets.
-allow su unlabeled:{ socket vsock_socket } rw_socket_perms_no_ioctl;
-')
diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts
index 20538a5..49ef362 100644
--- a/shared/sepolicy/vendor/file_contexts
+++ b/shared/sepolicy/vendor/file_contexts
@@ -62,6 +62,7 @@
/vendor/bin/rename_netiface u:object_r:rename_netiface_exec:s0
/vendor/bin/suspend_blocker u:object_r:suspend_blocker_exec:s0
/vendor/bin/hw/libcuttlefish-rild u:object_r:libcuttlefish_rild_exec:s0
+/vendor/bin/hw/android\.hardware\.camera\.provider@2\.7-external-vsock-service u:object_r:hal_camera_default_exec:s0
/vendor/bin/hw/android\.hardware\.camera\.provider@2\.7-service-google u:object_r:hal_camera_default_exec:s0
/vendor/bin/hw/android\.hardware\.camera\.provider@2\.7-service-google-lazy u:object_r:hal_camera_default_exec:s0
/vendor/bin/hw/android\.hardware\.power\.stats@1\.0-service\.mock u:object_r:hal_power_stats_default_exec:s0
@@ -100,6 +101,7 @@
/vendor/lib(64)?/libglapi.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/dri/.* u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/hw/android\.hardware\.graphics\.mapper@4\.0-impl\.minigbm\.so u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libminigbm_gralloc.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/hw/android\.hardware\.health@2\.0-impl-2\.1-cuttlefish\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/hw/vulkan.pastel.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libcuttlefish_fs.so u:object_r:same_process_hal_file:s0
diff --git a/shared/sepolicy/vendor/genfs_contexts b/shared/sepolicy/vendor/genfs_contexts
index 22cb59f..3a18743 100644
--- a/shared/sepolicy/vendor/genfs_contexts
+++ b/shared/sepolicy/vendor/genfs_contexts
@@ -23,9 +23,9 @@
dnl # $2 = rtc number (decimal)
dnl # $3 = rtc wakeup offset (decimal)
pushdef(`cf_rtc_wakeup_alarmtimer', `dnl
-genfscon sysfs $1/wakeup/wakeup$3 u:object_r:sysfs_wakeup:s0
+genfscon sysfs $1/wakeup u:object_r:sysfs_wakeup:s0
genfscon sysfs $1/rtc/rtc$2/wakeup`'eval($3 + 1)`' u:object_r:sysfs_wakeup:s0 # <= 5.5
-genfscon sysfs $1/rtc/rtc$2/alarmtimer.0.auto/wakeup/wakeup`'eval($3 + 1)`' u:object_r:sysfs_wakeup:s0 # >5.5
+genfscon sysfs $1/rtc/rtc$2/alarmtimer.0.auto/wakeup u:object_r:sysfs_wakeup:s0 # >5.5
dnl')dnl
dnl
# crosvm (x86)
@@ -34,9 +34,10 @@
## find /sys/devices/platform/* -type d -name 'rtc[0-9]' | sed 's,/rtc[0-9],,'
genfscon sysfs /devices/platform/rtc_cmos/rtc u:object_r:sysfs_rtc:s0
## find /sys/devices/platform/* -type d -name 'wakeup[0-9]'
-cf_rtc_wakeup_alarmtimer(/devices/platform/rtc_cmos, 0, 0)
-genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0
-genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup3 u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/wakeup u:object_r:sysfs_wakeup:s0
+cf_rtc_wakeup_alarmtimer(/devices/platform/rtc_cmos, 0, 1)
+## currently disabled
+#genfscon sysfs /devices/LNXSYSTM:00/GFSH0001:00/wakeup u:object_r:sysfs_wakeup:s0
# crosvm (arm64)
cf_pci_block_device(/devices/platform/10000.pci, 0x6, 4)
@@ -45,8 +46,6 @@
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
-cf_rtc_wakeup_alarmtimer(/devices/platform/rtc-test.1, 2, 0)
-genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0
# qemu (x86)
cf_pci_block_device(/devices/pci0000:00, 0x7, 5)
@@ -54,8 +53,6 @@
genfscon sysfs /devices/pnp0/00:04/rtc u:object_r:sysfs_rtc:s0
## find /sys/devices/platform/* -type d -name 'wakeup[0-9][0-9]'
cf_rtc_wakeup_alarmtimer(/devices/pnp0/00:04, 0, 19)
-genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup21 u:object_r:sysfs_wakeup:s0
-genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup22 u:object_r:sysfs_wakeup:s0
# qemu (arm64)
cf_pci_block_device(/devices/platform/4010000000.pcie/pci0000:00, 0x6, 4)
@@ -74,6 +71,8 @@
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
+cf_rtc_wakeup_alarmtimer(/devices/platform/rtc-test.1, 2, 0)
+genfscon sysfs /devices/platform/rtc-test.2/wakeup u:object_r:sysfs_wakeup:s0
dnl
popdef(`cf_pci_block_device')dnl
popdef(`cf_pci_gpu_device')dnl
diff --git a/shared/sepolicy/vendor/hal_camera_default.te b/shared/sepolicy/vendor/hal_camera_default.te
index 6bf571c..e4dac76 100644
--- a/shared/sepolicy/vendor/hal_camera_default.te
+++ b/shared/sepolicy/vendor/hal_camera_default.te
@@ -10,3 +10,6 @@
hal_client_domain(hal_camera_default, hal_thermal)
gpu_access(hal_camera_default)
+
+# Vsocket camera
+allow hal_camera_default self:vsock_socket { accept bind create getopt listen read write };
diff --git a/shared/sepolicy/vendor/hal_graphics_composer_default.te b/shared/sepolicy/vendor/hal_graphics_composer_default.te
index 807ec73..2dcee24 100644
--- a/shared/sepolicy/vendor/hal_graphics_composer_default.te
+++ b/shared/sepolicy/vendor/hal_graphics_composer_default.te
@@ -3,6 +3,8 @@
allow hal_graphics_composer_default self:netlink_kobject_uevent_socket { bind create read };
+allow hal_graphics_composer_default kmsg_device:chr_file w_file_perms;
+
# Supress warnings for drm_hwcomposer trying to read some vendor.hwc.*
# properties as Cuttlefish never configures these properties.
dontaudit hal_graphics_composer_default default_prop:file read;
\ No newline at end of file
diff --git a/shared/sepolicy/vendor/shell.te b/shared/sepolicy/vendor/shell.te
index cc26032..2ef1897 100644
--- a/shared/sepolicy/vendor/shell.te
+++ b/shared/sepolicy/vendor/shell.te
@@ -1,5 +1,2 @@
allow shell serial_device:chr_file { getattr ioctl read write };
allow shell cuttlefish_sensor_injection_exec:file rx_file_perms;
-
-# TODO(b/130668487): Label the vsock sockets.
-allow shell adbd:{ socket vsock_socket } rw_socket_perms_no_ioctl;
diff --git a/shared/tv/overlay/frameworks/base/core/res/res/values/config.xml b/shared/tv/overlay/frameworks/base/core/res/res/values/config.xml
index 5ec4e78..46c64b7 100644
--- a/shared/tv/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/shared/tv/overlay/frameworks/base/core/res/res/values/config.xml
@@ -17,8 +17,8 @@
<resources>
<!-- Maximum number of supported users -->
<integer name="config_multiuserMaximumUsers">4</integer>
- <!-- Restricting eth2 -->
+ <!-- Restricting eth1 -->
<string-array translatable="false" name="config_ethernet_interfaces">
- <item>eth2;11,12,14;;</item>
+ <item>eth1;11,12,14;;</item>
</string-array>
</resources>
diff --git a/tests/hal/hal_implementation_test.cpp b/tests/hal/hal_implementation_test.cpp
index e7bdc72..d05f077 100644
--- a/tests/hal/hal_implementation_test.cpp
+++ b/tests/hal/hal_implementation_test.cpp
@@ -34,11 +34,11 @@
"[email protected]",
"[email protected]",
"[email protected]",
- "[email protected]",
+ "[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
- "[email protected]",
+ "[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
@@ -59,7 +59,6 @@
"[email protected]",
"[email protected]",
"[email protected]",
- "[email protected]",
"[email protected]",
"[email protected]",
"[email protected]", // converted to AIDL, see b/177470478
diff --git a/vsoc_arm64/kernel.mk b/vsoc_arm64/kernel.mk
index c27f7cb..19d1b7c 100644
--- a/vsoc_arm64/kernel.mk
+++ b/vsoc_arm64/kernel.mk
@@ -14,5 +14,6 @@
# limitations under the License.
TARGET_KERNEL_USE ?= 5.10
+TARGET_KERNEL_PATH ?= kernel/prebuilts/$(TARGET_KERNEL_USE)/arm64/kernel-$(TARGET_KERNEL_USE)
-PRODUCT_COPY_FILES += kernel/prebuilts/$(TARGET_KERNEL_USE)/arm64/kernel-$(TARGET_KERNEL_USE):kernel
+PRODUCT_COPY_FILES += $(TARGET_KERNEL_PATH):kernel
diff --git a/vsoc_arm_only/kernel.mk b/vsoc_arm_only/kernel.mk
index 50b4e0b..f7472e7 100644
--- a/vsoc_arm_only/kernel.mk
+++ b/vsoc_arm_only/kernel.mk
@@ -13,4 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-PRODUCT_COPY_FILES += device/google/cuttlefish_prebuilts/kernel/5.4-arm/kernel-5.4:kernel
+TARGET_KERNEL_PATH ?= device/google/cuttlefish_prebuilts/kernel/5.4-arm/kernel-5.4
+
+PRODUCT_COPY_FILES += $(TARGET_KERNEL_PATH):kernel
diff --git a/vsoc_x86/auto/device.mk b/vsoc_x86/auto/device.mk
index c3ccba3..e0d5bad8 100644
--- a/vsoc_x86/auto/device.mk
+++ b/vsoc_x86/auto/device.mk
@@ -23,10 +23,6 @@
PRODUCT_MANUFACTURER := Google
PRODUCT_MODEL := Cuttlefish x86 auto
-# Whitelisted packages per user type
-PRODUCT_COPY_FILES += \
- device/google/cuttlefish/vsoc_x86/auto/preinstalled-packages-product-car-cuttlefish.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/sysconfig/preinstalled-packages-product-car-cuttlefish.xml
-
PRODUCT_VENDOR_PROPERTIES += \
ro.soc.manufacturer=$(PRODUCT_MANUFACTURER) \
ro.soc.model=$(PRODUCT_DEVICE)
diff --git a/vsoc_x86_64/kernel.mk b/vsoc_x86_64/kernel.mk
index 5dded66..112eb25 100644
--- a/vsoc_x86_64/kernel.mk
+++ b/vsoc_x86_64/kernel.mk
@@ -14,5 +14,6 @@
# limitations under the License.
TARGET_KERNEL_USE ?= 5.10
+TARGET_KERNEL_PATH ?= kernel/prebuilts/$(TARGET_KERNEL_USE)/x86_64/kernel-$(TARGET_KERNEL_USE)
-PRODUCT_COPY_FILES += kernel/prebuilts/$(TARGET_KERNEL_USE)/x86_64/kernel-$(TARGET_KERNEL_USE):kernel
+PRODUCT_COPY_FILES += $(TARGET_KERNEL_PATH):kernel
diff --git a/vsoc_x86_64/phone/aosp_cf_foldable.mk b/vsoc_x86_64/phone/aosp_cf_foldable.mk
index 89b0ea3..c0872d8 100644
--- a/vsoc_x86_64/phone/aosp_cf_foldable.mk
+++ b/vsoc_x86_64/phone/aosp_cf_foldable.mk
@@ -21,7 +21,15 @@
# Include the device state configuration for a foldable device.
PRODUCT_COPY_FILES += \
- device/google/cuttlefish/shared/foldable/device_state_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/devicestate/device_state_configuration.xml
+ device/google/cuttlefish/shared/foldable/device_state_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/devicestate/device_state_configuration.xml \
+ device/google/cuttlefish/shared/foldable/display_layout_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/displayconfig/display_layout_configuration.xml \
+ device/google/cuttlefish/shared/foldable/display_settings.xml:$(TARGET_COPY_OUT_VENDOR)/etc/display_settings.xml \
+
+# Sidecar and window extensions enhance multi-display, tablet, and foldable support.
+PRODUCT_PACKAGES += \
+ androidx.window.extensions \
+ androidx.window.sidecar \
+
# Include RRO settings that specify the fold states and screen information.
DEVICE_PACKAGE_OVERLAYS += device/google/cuttlefish/shared/foldable/overlay
# Include the foldable `launch_cvd --config foldable` option.
diff --git a/vsoc_x86_only/kernel.mk b/vsoc_x86_only/kernel.mk
index 23cf086..d06c0a1 100644
--- a/vsoc_x86_only/kernel.mk
+++ b/vsoc_x86_only/kernel.mk
@@ -14,5 +14,6 @@
# limitations under the License.
TARGET_KERNEL_USE ?= 5.10
+TARGET_KERNEL_PATH ?= device/google/cuttlefish_prebuilts/kernel/$(TARGET_KERNEL_USE)-i686/kernel-$(TARGET_KERNEL_USE)
-PRODUCT_COPY_FILES += device/google/cuttlefish_prebuilts/kernel/$(TARGET_KERNEL_USE)-i686/kernel-$(TARGET_KERNEL_USE):kernel
+PRODUCT_COPY_FILES +=$(TARGET_KERNEL_PATH):kernel