Allow the vnc server to run when using crosvm as vmm
The vnc server sends input through unix sockets to crosvm's virtio
input devices and receives screen updates through a vsock socket.
Performance is not the best, but it's usable.
Bug: 128852363
Bug: 127361363
Test: run locally
Change-Id: I2b2be2c3f338e7624e390879293ce61186dded38
diff --git a/guest/hals/hwcomposer/cutf_cvm/Android.bp b/guest/hals/hwcomposer/cutf_cvm/Android.bp
index 7bf1faa..9e3962b 100644
--- a/guest/hals/hwcomposer/cutf_cvm/Android.bp
+++ b/guest/hals/hwcomposer/cutf_cvm/Android.bp
@@ -18,9 +18,27 @@
relative_install_path: "hw",
defaults: ["cuttlefish_guest_only"],
vendor: true,
- srcs: ["hwcomposer.cpp"],
- include_dirs: ["device/google/cuttlefish_common"],
+ srcs: [
+ "hwcomposer.cpp",
+ "geometry_utils.cpp",
+ "vsoc_composer.cpp",
+ "base_composer.cpp",
+ ],
+ include_dirs: [
+ "device/google/cuttlefish_common",
+ ],
export_include_dirs: ["."],
static_libs: ["libyuv_static", "hwcomposer_common"],
- shared_libs: ["liblog", "libhardware"],
+ shared_libs: [
+ "liblog",
+ "libhardware",
+ "libbase",
+ "libcutils",
+ "libutils",
+ "libsync",
+ "libhardware",
+ "libjpeg",
+ "libcuttlefish_utils",
+ "libcuttlefish_fs",
+ ],
}
diff --git a/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp b/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp
new file mode 100644
index 0000000..cda2750
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base_composer.h"
+
+#include <string.h>
+
+#include <cutils/properties.h>
+#include <hardware/gralloc.h>
+#include <log/log.h>
+
+#include <common/libs/utils/size_utils.h>
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+
+namespace cvd {
+
+BaseComposer::BaseComposer(int64_t vsync_base_timestamp)
+ : vsync_base_timestamp_(vsync_base_timestamp) {
+ vsync_period_ns_ = 1000000000 / frame_buffer_.refresh_rate();
+ hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+ reinterpret_cast<const hw_module_t**>(&gralloc_module_));
+}
+
+BaseComposer::~BaseComposer() {}
+
+void BaseComposer::Dump(char* buff __unused, int buff_len __unused) {}
+
+int BaseComposer::PostFrameBufferTarget(buffer_handle_t buffer_handle) {
+ int fb_index = frame_buffer_.NextScreenBuffer();
+ void* frame_buffer = frame_buffer_.GetBuffer(fb_index);
+ const private_handle_t* p_handle =
+ reinterpret_cast<const private_handle_t*>(buffer_handle);
+ void* buffer;
+ int retval = gralloc_module_->lock(gralloc_module_, buffer_handle,
+ GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
+ p_handle->x_res, p_handle->y_res, &buffer);
+ if (retval != 0) {
+ ALOGE("Got error code %d from lock function", retval);
+ return -1;
+ }
+ memcpy(frame_buffer, buffer, frame_buffer_.buffer_size());
+ frame_buffer_.Broadcast(fb_index);
+ return 0;
+} // namespace cvd
+
+int BaseComposer::PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+ // find unsupported overlays
+ for (size_t i = 0; i < num_layers; i++) {
+ if (IS_TARGET_FRAMEBUFFER(layers[i].compositionType)) {
+ continue;
+ }
+ layers[i].compositionType = HWC_FRAMEBUFFER;
+ }
+ return 0;
+}
+
+int BaseComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+ for (size_t idx = 0; idx < num_layers; idx++) {
+ if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+ return PostFrameBufferTarget(layers[idx].handle);
+ }
+ }
+ return -1;
+}
+
+FrameBuffer::FrameBuffer()
+ : screen_server_(cvd::SharedFD::VsockClient(
+ 2, property_get_int32("ro.boot.vsock_frames_port", 5580),
+ SOCK_STREAM)),
+ broadcast_thread_([this]() { BroadcastLoop(); }) {
+ // TODO(b/128842613): Get this info from the configuration server
+ int32_t screen_params[4];
+ auto res = screen_server_->Read(screen_params, sizeof(screen_params));
+ if (res != sizeof(screen_params)) {
+ LOG(ERROR) << "Unable to get screen configuration parameters from screen "
+ << "server (" << res << "): " << screen_server_->StrError();
+ return;
+ }
+ x_res_ = screen_params[0];
+ y_res_ = screen_params[1];
+ dpi_ = screen_params[2];
+ refresh_rate_ = screen_params[3];
+ inner_buffer_ = std::vector<char>(FrameBuffer::buffer_size() * 8);
+}
+
+FrameBuffer::~FrameBuffer() {
+ running_ = false;
+ broadcast_thread_.join();
+}
+
+int FrameBuffer::NextScreenBuffer() {
+ int num_buffers = inner_buffer_.size() / buffer_size();
+ last_frame_buffer_ =
+ num_buffers > 0 ? (last_frame_buffer_ + 1) % num_buffers : -1;
+ return last_frame_buffer_;
+}
+
+void FrameBuffer::BroadcastLoop() {
+ int32_t current_seq = 0;
+ int32_t current_offset;
+ while (running_) {
+ {
+ std::unique_lock<std::mutex> lock(mutex_);
+ while (current_seq == current_seq_) {
+ cond_var_.wait(lock);
+ }
+ current_offset = current_offset_;
+ current_seq = current_seq_;
+ }
+ int32_t size = buffer_size();
+ screen_server_->Write(&size, sizeof(size));
+ auto buff = static_cast<char*>(GetBuffer(current_offset));
+ while (size > 0) {
+ auto written = screen_server_->Write(buff, size);
+ size -= written;
+ buff += written;
+ }
+ }
+}
+
+void FrameBuffer::Broadcast(int32_t offset) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ current_offset_ = offset;
+ current_seq_++;
+ cond_var_.notify_all();
+}
+void* FrameBuffer::GetBuffer(int fb_index) {
+ return &inner_buffer_[buffer_size() * fb_index];
+}
+size_t FrameBuffer::buffer_size() {
+ return (line_length() * y_res()) + 4;
+}
+int32_t FrameBuffer::x_res() { return x_res_; }
+int32_t FrameBuffer::y_res() { return y_res_; }
+int32_t FrameBuffer::line_length() {
+ return cvd::AlignToPowerOf2(x_res() * bytes_per_pixel(), 4);
+}
+int32_t FrameBuffer::bytes_per_pixel() { return 4; }
+int32_t FrameBuffer::dpi() { return dpi_; }
+int32_t FrameBuffer::refresh_rate() { return refresh_rate_; }
+
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/base_composer.h b/guest/hals/hwcomposer/cutf_cvm/base_composer.h
new file mode 100644
index 0000000..5a23176
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/base_composer.h
@@ -0,0 +1,98 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <hardware/gralloc.h>
+#include <common/libs/fs/shared_fd.h>
+#include "hwcomposer.h"
+
+namespace cvd {
+
+class FrameBuffer{
+ public:
+ FrameBuffer();
+ ~FrameBuffer();
+
+ void Broadcast(int32_t offset);
+ int NextScreenBuffer();
+ void* GetBuffer(int fb_index);
+ size_t buffer_size();
+ int32_t x_res();
+ int32_t y_res();
+ int32_t line_length();
+ int32_t bytes_per_pixel();
+ int32_t dpi();
+ int32_t refresh_rate();
+ private:
+ void BroadcastLoop();
+
+ std::vector<char> inner_buffer_;
+ int last_frame_buffer_ = 0;
+ cvd::SharedFD screen_server_;
+ std::thread broadcast_thread_;
+ int32_t current_offset_ = 0;
+ int32_t current_seq_ = 0;
+ std::mutex mutex_;
+ std::condition_variable cond_var_;
+ bool running_ = true;
+ int32_t x_res_{720};
+ int32_t y_res_{1280};
+ int32_t dpi_{160};
+ int32_t refresh_rate_{60};
+};
+
+class BaseComposer {
+ public:
+ BaseComposer(int64_t vsync_base_timestamp);
+ ~BaseComposer();
+
+ // Sets the composition type of each layer and returns the number of layers
+ // to be composited by the hwcomposer.
+ int PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers);
+ // Returns 0 if successful.
+ int SetLayers(size_t num_layers, vsoc_hwc_layer* layers);
+ void Dump(char* buff, int buff_len);
+
+ int32_t x_res() {
+ return frame_buffer_.x_res();
+ }
+ int32_t y_res() {
+ return frame_buffer_.y_res();
+ }
+ int32_t dpi() {
+ return frame_buffer_.dpi();
+ }
+ int32_t refresh_rate() {
+ return frame_buffer_.refresh_rate();
+ }
+
+ protected:
+ const gralloc_module_t* gralloc_module_;
+ int64_t vsync_base_timestamp_;
+ int32_t vsync_period_ns_;
+ FrameBuffer frame_buffer_;
+
+ private:
+ // Returns buffer offset or negative on error.
+ int PostFrameBufferTarget(buffer_handle_t handle);
+};
+
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp
new file mode 100644
index 0000000..75b7a61
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "geometry_utils.h"
+#include <algorithm>
+#include <utility>
+
+namespace cvd {
+
+bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2) {
+ int left1 = layer1.displayFrame.left;
+ int right1 = layer1.displayFrame.right;
+ int top1 = layer1.displayFrame.top;
+ int bottom1 = layer1.displayFrame.bottom;
+
+ int left2 = layer2.displayFrame.left;
+ int right2 = layer2.displayFrame.right;
+ int top2 = layer2.displayFrame.top;
+ int bottom2 = layer2.displayFrame.bottom;
+
+ bool overlap_x = left1 < right2 && left2 < right1;
+ bool overlap_y = top1 < bottom2 && top2 < bottom1;
+
+ return overlap_x && overlap_y;
+}
+
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
new file mode 100644
index 0000000..937283f
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
@@ -0,0 +1,24 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hwcomposer.h"
+
+namespace cvd {
+
+bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
+
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
index 954688e..3db7f80 100644
--- a/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
+++ b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,207 +14,470 @@
* limitations under the License.
*/
+// Versions of hwcomposer we implement:
+// JB: 0.3
+// JB-MR1 to N : 1.1
+// N-MR1 to ... : We report 1.1 but SurfaceFlinger has the option to use an
+// adapter to treat our 1.1 hwcomposer as a 2.0. If SF stops using that adapter
+// to support 1.1 implementations it can be copied into cuttlefish from
+// frameworks/native/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.*
+
#define LOG_TAG "hwc.cf_x86"
-#define HWC_REMOVE_DEPRECATED_VERSIONS 1
-#define IS_TARGET_FRAMEBUFFER(x) ((x) == HWC_FRAMEBUFFER_TARGET)
-#define IS_PRIMARY_DISPLAY(x) ((x) == HWC_DISPLAY_PRIMARY)
-#define CUTF_CVM_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_1_1
+
+#include <guest/libs/platform_support/api_level_fixes.h>
#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <poll.h>
#include <pthread.h>
#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
#include <string>
-#include "log/log.h"
+#define HWC_REMOVE_DEPRECATED_VERSIONS 1
+
+#include <cutils/compiler.h>
+#include <log/log.h>
+#include <cutils/properties.h>
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
#include "guest/hals/hwcomposer/common/hwcomposer.h"
-#include "guest/libs/platform_support/api_level_fixes.h"
-#include "hardware/hwcomposer.h"
-#include "hardware/hwcomposer_defs.h"
+#include <sync/sync.h>
-typedef hwc_composer_device_1_t cutf_cvm_hwc_device;
+#include "base_composer.h"
+#include "geometry_utils.h"
+#include "hwcomposer.h"
+#include "vsoc_composer.h"
-struct cutf_cvm_hwc_composer_device_1_t {
- cutf_cvm_hwc_device base;
+#ifdef USE_OLD_HWCOMPOSER
+typedef cvd::BaseComposer InnerComposerType;
+#else
+typedef cvd::VSoCComposer InnerComposerType;
+#endif
+
+typedef InnerComposerType ComposerType;
+
+struct vsoc_hwc_composer_device_1_t {
+ vsoc_hwc_device base;
cvd::hwc_composer_device_data_t vsync_data;
+ ComposerType* composer;
};
-static int cutf_cvm_hwc_prepare(cutf_cvm_hwc_device* dev __unused, size_t numDisplays,
- hwc_display_contents_1_t** displays) {
- hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
- if (!numDisplays || !displays || !list)
- return 0;
+namespace {
- for (size_t i = 0; i < list->numHwLayers; i++) {
- if (IS_TARGET_FRAMEBUFFER(list->hwLayers[i].compositionType)) {
- continue;
- }
- list->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
+std::string CompositionString(int type) {
+ switch (type) {
+ case HWC_FRAMEBUFFER:
+ return "Framebuffer";
+ case HWC_OVERLAY:
+ return "Overlay";
+ case HWC_BACKGROUND:
+ return "Background";
+ case HWC_FRAMEBUFFER_TARGET:
+ return "FramebufferTarget";
+#if VSOC_PLATFORM_SDK_AFTER(K)
+ case HWC_SIDEBAND:
+ return "Sideband";
+ case HWC_CURSOR_OVERLAY:
+ return "CursorOverlay";
+#endif
+ default:
+ return std::string("Unknown (") + std::to_string(type) + ")";
}
+}
+
+void LogLayers(int num_layers, vsoc_hwc_layer* layers, int invalid) {
+ ALOGE("Layers:");
+ for (int idx = 0; idx < num_layers; ++idx) {
+ std::string log_line;
+ if (idx == invalid) {
+ log_line = "Invalid layer: ";
+ }
+ log_line +=
+ "Composition Type: " + CompositionString(layers[idx].compositionType);
+ ALOGE("%s", log_line.c_str());
+ }
+}
+
+// Ensures that the layer does not include any inconsistencies
+bool IsValidLayer(const vsoc_hwc_layer& layer) {
+ if (layer.flags & HWC_SKIP_LAYER) {
+ // A layer we are asked to skip validate should not be marked as skip
+ ALOGE("%s: Layer is marked as skip", __FUNCTION__);
+ return false;
+ }
+ // Check displayFrame
+ if (layer.displayFrame.left > layer.displayFrame.right ||
+ layer.displayFrame.top > layer.displayFrame.bottom) {
+ ALOGE(
+ "%s: Malformed rectangle (displayFrame): [left = %d, right = %d, top = "
+ "%d, bottom = %d]",
+ __FUNCTION__, layer.displayFrame.left, layer.displayFrame.right,
+ layer.displayFrame.top, layer.displayFrame.bottom);
+ return false;
+ }
+ // Validate the handle
+ if (private_handle_t::validate(layer.handle) != 0) {
+ ALOGE("%s: Layer contains an invalid gralloc handle.", __FUNCTION__);
+ return false;
+ }
+ const private_handle_t* p_handle =
+ reinterpret_cast<const private_handle_t*>(layer.handle);
+ // Check sourceCrop
+ if (layer.sourceCrop.left > layer.sourceCrop.right ||
+ layer.sourceCrop.top > layer.sourceCrop.bottom) {
+ ALOGE(
+ "%s: Malformed rectangle (sourceCrop): [left = %d, right = %d, top = "
+ "%d, bottom = %d]",
+ __FUNCTION__, layer.sourceCrop.left, layer.sourceCrop.right,
+ layer.sourceCrop.top, layer.sourceCrop.bottom);
+ return false;
+ }
+ if (layer.sourceCrop.left < 0 || layer.sourceCrop.top < 0 ||
+ layer.sourceCrop.right > p_handle->x_res ||
+ layer.sourceCrop.bottom > p_handle->y_res) {
+ ALOGE(
+ "%s: Invalid sourceCrop for buffer handle: sourceCrop = [left = %d, "
+ "right = %d, top = %d, bottom = %d], handle = [width = %d, height = "
+ "%d]",
+ __FUNCTION__, layer.sourceCrop.left, layer.sourceCrop.right,
+ layer.sourceCrop.top, layer.sourceCrop.bottom, p_handle->x_res,
+ p_handle->y_res);
+ return false;
+ }
+ return true;
+}
+
+bool IsValidComposition(int num_layers, vsoc_hwc_layer* layers, bool on_set) {
+ if (num_layers == 0) {
+ ALOGE("Composition requested with 0 layers");
+ return false;
+ }
+ // Sometimes the hwcomposer receives a prepare and set calls with no other
+ // layer than the FRAMEBUFFER_TARGET with a null handler. We treat this case
+ // independently as a valid composition, but issue a warning about it.
+ if (num_layers == 1 && layers[0].compositionType == HWC_FRAMEBUFFER_TARGET &&
+ layers[0].handle == NULL) {
+ ALOGW("Received request for empty composition, treating as valid noop");
+ return true;
+ }
+ // The FRAMEBUFFER_TARGET layer needs to be sane only if
+ // there is at least one layer marked HWC_FRAMEBUFFER or if there is no layer
+ // marked HWC_OVERLAY (i.e some layers where composed with OpenGL, no layer
+ // marked overlay or framebuffer means that surfaceflinger decided to go for
+ // OpenGL without asking the hwcomposer first)
+ bool check_fb_target = true;
+ for (int idx = 0; idx < num_layers; ++idx) {
+ if (layers[idx].compositionType == HWC_FRAMEBUFFER) {
+ // There is at least one, so it needs to be checked.
+ // It may have been set to false before, so ensure it's set to true.
+ check_fb_target = true;
+ break;
+ }
+ if (layers[idx].compositionType == HWC_OVERLAY) {
+ // At least one overlay, we may not need to.
+ check_fb_target = false;
+ }
+ }
+
+ for (int idx = 0; idx < num_layers; ++idx) {
+ switch (layers[idx].compositionType) {
+ case HWC_FRAMEBUFFER_TARGET:
+ // In the call to prepare() the framebuffer target does not have a valid
+ // buffer_handle, so we don't validate it yet.
+ if (on_set && check_fb_target && !IsValidLayer(layers[idx])) {
+ ALOGE("%s: Invalid layer found", __FUNCTION__);
+ LogLayers(num_layers, layers, idx);
+ return false;
+ }
+ break;
+ case HWC_OVERLAY:
+ if (!(layers[idx].flags & HWC_SKIP_LAYER) &&
+ !IsValidLayer(layers[idx])) {
+ ALOGE("%s: Invalid layer found", __FUNCTION__);
+ LogLayers(num_layers, layers, idx);
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+}
+
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+static int vsoc_hwc_prepare(vsoc_hwc_device* dev, hwc_layer_list_t* list) {
+#else
+static int vsoc_hwc_prepare(vsoc_hwc_device* dev, size_t numDisplays,
+ hwc_display_contents_1_t** displays) {
+ if (!numDisplays || !displays) return 0;
+
+ hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
+
+ if (!list) return 0;
+#endif
+ if (!IsValidComposition(list->numHwLayers, &list->hwLayers[0], false)) {
+ LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
+ return -1;
+ }
+ reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->PrepareLayers(
+ list->numHwLayers, &list->hwLayers[0]);
return 0;
}
-static int cutf_cvm_hwc_set(cutf_cvm_hwc_device* dev __unused, size_t numDisplays __unused,
- hwc_display_contents_1_t** displays __unused) {
- // TODO: b/125482910
- return 0;
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+int vsoc_hwc_set(struct hwc_composer_device* dev, hwc_display_t dpy,
+ hwc_surface_t sur, hwc_layer_list_t* list) {
+ if (list->numHwLayers == 1 &&
+ layers[0].compositionType == HWC_FRAMEBUFFER_TARGET) {
+ ALOGW("Received request for empty composition, treating as valid noop");
+ return 0;
+ }
+ if (!IsValidComposition(list->numHwLayers, &list->hwLayers[0], true)) {
+ LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
+ return -1;
+ }
+ return reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)
+ ->composer->SetLayers(list->numHwLayers, &list->hwLayers[0]);
}
+#else
+static int vsoc_hwc_set(vsoc_hwc_device* dev, size_t numDisplays,
+ hwc_display_contents_1_t** displays) {
+ if (!numDisplays || !displays) return 0;
-static void cutf_cvm_hwc_register_procs(cutf_cvm_hwc_device* dev,
+ hwc_display_contents_1_t* contents = displays[HWC_DISPLAY_PRIMARY];
+ if (!contents) return 0;
+
+ vsoc_hwc_layer* layers = &contents->hwLayers[0];
+ if (contents->numHwLayers == 1 &&
+ layers[0].compositionType == HWC_FRAMEBUFFER_TARGET) {
+ ALOGW("Received request for empty composition, treating as valid noop");
+ return 0;
+ }
+ if (!IsValidComposition(contents->numHwLayers, layers, true)) {
+ LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
+ return -1;
+ }
+ int retval =
+ reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->SetLayers(
+ contents->numHwLayers, layers);
+
+ int closedFds = 0;
+ for (size_t index = 0; index < contents->numHwLayers; ++index) {
+ if (layers[index].acquireFenceFd != -1) {
+ close(layers[index].acquireFenceFd);
+ layers[index].acquireFenceFd = -1;
+ ++closedFds;
+ }
+ }
+ if (closedFds) {
+ ALOGI("Saw %zu layers, closed=%d", contents->numHwLayers, closedFds);
+ }
+
+ // TODO(ghartman): This should be set before returning. On the next set it
+ // should be signalled when we load the new frame.
+ contents->retireFenceFd = -1;
+ return retval;
+}
+#endif
+
+static void vsoc_hwc_register_procs(vsoc_hwc_device* dev,
const hwc_procs_t* procs) {
- struct cutf_cvm_hwc_composer_device_1_t* pdev =
- (struct cutf_cvm_hwc_composer_device_1_t*)dev;
+ struct vsoc_hwc_composer_device_1_t* pdev =
+ (struct vsoc_hwc_composer_device_1_t*)dev;
pdev->vsync_data.procs = procs;
}
-static int cutf_cvm_hwc_query(cutf_cvm_hwc_device* dev __unused, int what __unused, int* value) {
- // TODO: b/125482910
- struct cutf_cvm_hwc_composer_device_1_t* pdev =
- (struct cutf_cvm_hwc_composer_device_1_t*)dev;
+static int vsoc_hwc_query(vsoc_hwc_device* dev, int what, int* value) {
+ struct vsoc_hwc_composer_device_1_t* pdev =
+ (struct vsoc_hwc_composer_device_1_t*)dev;
+
switch (what) {
case HWC_BACKGROUND_LAYER_SUPPORTED:
- *value = 0;
+ // we support the background layer
+ value[0] = 0;
break;
case HWC_VSYNC_PERIOD:
- *value = pdev->vsync_data.vsync_period_ns;
+ value[0] = pdev->vsync_data.vsync_period_ns;
break;
default:
// unsupported query
- ALOGE("%s: badness unsupported query what=%d", __FUNCTION__, what);
+ ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
return -EINVAL;
}
return 0;
}
-static int cutf_cvm_hwc_event_control(
- cutf_cvm_hwc_device* /*dev*/, int /*dpy*/, int event __unused, int /*enabled*/) {
- // TODO: b/125482910
+static int vsoc_hwc_event_control(
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+ vsoc_hwc_device* /*dev*/, int event, int /*enabled*/) {
+#else
+ vsoc_hwc_device* /*dev*/, int /*dpy*/, int event, int /*enabled*/) {
+#endif
+
+ if (event == HWC_EVENT_VSYNC) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vsoc_hwc_blank(vsoc_hwc_device* /*dev*/, int disp, int /*blank*/) {
+ if (!IS_PRIMARY_DISPLAY(disp)) return -EINVAL;
return 0;
}
-static int cutf_cvm_hwc_blank(cutf_cvm_hwc_device* /*dev*/, int disp __unused, int /*blank*/) {
- // TODO: b/125482910
- return 0;
+static void vsoc_hwc_dump(vsoc_hwc_device* dev, char* buff, int buff_len) {
+ reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->Dump(
+ buff, buff_len);
}
-static void cutf_cvm_hwc_dump(cutf_cvm_hwc_device* dev __unused, char* buff __unused, int buff_len __unused) {
- // TODO: b/125482910
+static int vsoc_hwc_get_display_configs(vsoc_hwc_device* /*dev*/, int disp,
+ uint32_t* configs, size_t* numConfigs) {
+ if (*numConfigs == 0) return 0;
+
+ if (IS_PRIMARY_DISPLAY(disp)) {
+ configs[0] = 0;
+ *numConfigs = 1;
+ return 0;
+ }
+
+ return -EINVAL;
}
-static int cutf_cvm_hwc_get_display_configs(cutf_cvm_hwc_device* /*dev*/, int disp __unused,
- uint32_t* configs __unused, size_t* numConfigs __unused) {
- // TODO: b/125482910
- return 0;
-}
-
-static int32_t cutf_cvm_hwc_attribute(struct cutf_cvm_hwc_composer_device_1_t* pdev,
+#if VSOC_PLATFORM_SDK_AFTER(J)
+static int32_t vsoc_hwc_attribute(struct vsoc_hwc_composer_device_1_t* pdev,
const uint32_t attribute) {
- // TODO: b/125482910
switch (attribute) {
case HWC_DISPLAY_VSYNC_PERIOD:
return pdev->vsync_data.vsync_period_ns;
case HWC_DISPLAY_WIDTH:
- return 720;
+ return pdev->composer->x_res();
case HWC_DISPLAY_HEIGHT:
- return 1280;
+ return pdev->composer->y_res();
case HWC_DISPLAY_DPI_X:
- ALOGI("%s: Reporting DPI_X of %d", __FUNCTION__, 1);
+ ALOGI("Reporting DPI_X of %d", pdev->composer->dpi());
// The number of pixels per thousand inches
- return 240000;
+ return pdev->composer->dpi() * 1000;
case HWC_DISPLAY_DPI_Y:
- ALOGI("%s: Reporting DPI_Y of %d", __FUNCTION__, 1);
+ ALOGI("Reporting DPI_Y of %d", pdev->composer->dpi());
// The number of pixels per thousand inches
- return 240000;
+ return pdev->composer->dpi() * 1000;
default:
- ALOGE("%s: unknown display attribute %u", __FUNCTION__, attribute);
+ ALOGE("unknown display attribute %u", attribute);
return -EINVAL;
}
}
-static int cutf_cvm_hwc_get_display_attributes(cutf_cvm_hwc_device* dev __unused, int disp __unused,
+static int vsoc_hwc_get_display_attributes(vsoc_hwc_device* dev, int disp,
uint32_t config __unused,
- const uint32_t* attributes __unused,
- int32_t* values __unused) {
- struct cutf_cvm_hwc_composer_device_1_t* pdev =
- (struct cutf_cvm_hwc_composer_device_1_t*)dev;
+ const uint32_t* attributes,
+ int32_t* values) {
+ struct vsoc_hwc_composer_device_1_t* pdev =
+ (struct vsoc_hwc_composer_device_1_t*)dev;
if (!IS_PRIMARY_DISPLAY(disp)) {
- ALOGE("%s: unknown display type %u", __FUNCTION__, disp);
+ ALOGE("unknown display type %u", disp);
return -EINVAL;
}
for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
- values[i] = cutf_cvm_hwc_attribute(pdev, attributes[i]);
+ values[i] = vsoc_hwc_attribute(pdev, attributes[i]);
}
return 0;
}
+#endif
-static int cutf_cvm_hwc_close(hw_device_t* device) {
- struct cutf_cvm_hwc_composer_device_1_t* dev =
- (struct cutf_cvm_hwc_composer_device_1_t*)device;
+static int vsoc_hwc_close(hw_device_t* device) {
+ struct vsoc_hwc_composer_device_1_t* dev =
+ (struct vsoc_hwc_composer_device_1_t*)device;
+ ALOGE("vsoc_hwc_close");
+ pthread_kill(dev->vsync_data.vsync_thread, SIGTERM);
+ pthread_join(dev->vsync_data.vsync_thread, NULL);
+ delete dev->composer;
delete dev;
return 0;
}
-static int cutf_cvm_hwc_open(const struct hw_module_t* module, const char* name,
+static int vsoc_hwc_open(const struct hw_module_t* module, const char* name,
struct hw_device_t** device) {
ALOGI("%s", __FUNCTION__);
- if (strcmp(name, HWC_HARDWARE_COMPOSER) != 0) {
- ALOGE("%s: called with bad name %s", __FUNCTION__, name);
+ if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
+ ALOGE("%s called with bad name %s", __FUNCTION__, name);
return -EINVAL;
}
- cutf_cvm_hwc_composer_device_1_t* dev = new cutf_cvm_hwc_composer_device_1_t();
+ vsoc_hwc_composer_device_1_t* dev = new vsoc_hwc_composer_device_1_t();
if (!dev) {
- ALOGE("%s: failed to allocate dev", __FUNCTION__);
+ ALOGE("%s failed to allocate dev", __FUNCTION__);
return -ENOMEM;
}
- const int refreshRate = 60;
- const int nsPerSec = 1000000000;
- dev->vsync_data.vsync_period_ns = nsPerSec / refreshRate;
struct timespec rt;
- if (clock_gettime(CLOCK_MONOTONIC, &rt) != 0) {
- ALOGE("%s: error in clock_gettime: %s", __FUNCTION__, strerror(errno));
+ if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+ ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
+ strerror(errno));
}
- dev->vsync_data.vsync_base_timestamp = int64_t(rt.tv_sec) * nsPerSec + rt.tv_nsec;
+ dev->vsync_data.vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
dev->base.common.tag = HARDWARE_DEVICE_TAG;
- dev->base.common.version = CUTF_CVM_HWC_DEVICE_API_VERSION;
+ dev->base.common.version = VSOC_HWC_DEVICE_API_VERSION;
dev->base.common.module = const_cast<hw_module_t*>(module);
- dev->base.common.close = cutf_cvm_hwc_close;
+ dev->base.common.close = vsoc_hwc_close;
- dev->base.prepare = cutf_cvm_hwc_prepare;
- dev->base.set = cutf_cvm_hwc_set;
- dev->base.query = cutf_cvm_hwc_query;
- dev->base.registerProcs = cutf_cvm_hwc_register_procs;
- dev->base.dump = cutf_cvm_hwc_dump;
- dev->base.blank = cutf_cvm_hwc_blank;
- dev->base.eventControl = cutf_cvm_hwc_event_control;
- dev->base.getDisplayConfigs = cutf_cvm_hwc_get_display_configs;
- dev->base.getDisplayAttributes = cutf_cvm_hwc_get_display_attributes;
-
- int ret = pthread_create(&dev->vsync_data.vsync_thread, NULL, cvd::hwc_vsync_thread, &dev->vsync_data);
- if (ret != 0) {
- ALOGE("%s: failed to start vsync thread: %s", __FUNCTION__, strerror(ret));
- dev->base.common.close(&dev->base.common);
- return -ret;
+ dev->base.prepare = vsoc_hwc_prepare;
+ dev->base.set = vsoc_hwc_set;
+ dev->base.query = vsoc_hwc_query;
+ dev->base.registerProcs = vsoc_hwc_register_procs;
+ dev->base.dump = vsoc_hwc_dump;
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+ static hwc_methods_t hwc_methods = {vsoc_hwc_event_control};
+ dev->base.methods = &hwc_methods;
+#else
+ dev->base.blank = vsoc_hwc_blank;
+ dev->base.eventControl = vsoc_hwc_event_control;
+ dev->base.getDisplayConfigs = vsoc_hwc_get_display_configs;
+ dev->base.getDisplayAttributes = vsoc_hwc_get_display_attributes;
+#endif
+ dev->composer = new ComposerType(dev->vsync_data.vsync_base_timestamp);
+ dev->vsync_data.vsync_period_ns = 1000000000 / dev->composer->refresh_rate();
+ int ret = pthread_create(&dev->vsync_data.vsync_thread,
+ NULL, cvd::hwc_vsync_thread, &dev->vsync_data);
+ if (ret) {
+ ALOGE("failed to start vsync thread: %s", strerror(ret));
+ ret = -ret;
+ delete dev;
+ } else {
+ *device = &dev->base.common;
}
- *device = &dev->base.common;
return ret;
}
-static struct hw_module_methods_t cutf_cvm_hwc_module_methods = {
- cutf_cvm_hwc_open,
+static struct hw_module_methods_t vsoc_hwc_module_methods = {
+ vsoc_hwc_open,
};
hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG,
HWC_MODULE_API_VERSION_0_1,
HARDWARE_HAL_API_VERSION,
HWC_HARDWARE_MODULE_ID,
- "cutf_cvm hwcomposer module",
+ "VSOC hwcomposer module",
"Google",
- &cutf_cvm_hwc_module_methods,
+ &vsoc_hwc_module_methods,
NULL,
{0}}};
diff --git a/guest/hals/hwcomposer/cutf_cvm/hwcomposer.h b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.h
new file mode 100644
index 0000000..a6b8121
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.h
@@ -0,0 +1,35 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <guest/libs/platform_support/api_level_fixes.h>
+
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+typedef hwc_composer_device_t vsoc_hwc_device;
+typedef hwc_layer_t vsoc_hwc_layer;
+#define IS_TARGET_FRAMEBUFFER(x) false
+#define IS_PRIMARY_DISPLAY(x) true
+#define VSOC_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_0_3
+#else
+typedef hwc_composer_device_1_t vsoc_hwc_device;
+typedef hwc_layer_1_t vsoc_hwc_layer;
+#define IS_TARGET_FRAMEBUFFER(x) ((x) == HWC_FRAMEBUFFER_TARGET)
+#define IS_PRIMARY_DISPLAY(x) ((x) == HWC_DISPLAY_PRIMARY)
+#define VSOC_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_1_1
+#endif
diff --git a/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
new file mode 100644
index 0000000..5b55e84
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vsoc_composer.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <utility>
+#include <vector>
+
+#include <log/log.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <libyuv.h>
+#include <system/graphics.h>
+
+#include <common/libs/utils/size_utils.h>
+
+#include "geometry_utils.h"
+
+namespace cvd {
+
+namespace {
+
+bool LayerNeedsScaling(const vsoc_hwc_layer& layer) {
+ int from_w = layer.sourceCrop.right - layer.sourceCrop.left;
+ int from_h = layer.sourceCrop.bottom - layer.sourceCrop.top;
+ int to_w = layer.displayFrame.right - layer.displayFrame.left;
+ int to_h = layer.displayFrame.bottom - layer.displayFrame.top;
+
+ bool not_rot_scale = from_w != to_w || from_h != to_h;
+ bool rot_scale = from_w != to_h || from_h != to_w;
+
+ bool needs_rot = layer.transform & HAL_TRANSFORM_ROT_90;
+
+ return needs_rot ? rot_scale : not_rot_scale;
+}
+
+bool LayerNeedsBlending(const vsoc_hwc_layer& layer) {
+ return layer.blending != HWC_BLENDING_NONE;
+}
+
+bool LayerNeedsAttenuation(const vsoc_hwc_layer& layer) {
+ return layer.blending == HWC_BLENDING_COVERAGE;
+}
+
+struct BufferSpec;
+typedef int (*ConverterFunction)(const BufferSpec& src, const BufferSpec& dst,
+ bool v_flip);
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip);
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool v_flip);
+ConverterFunction GetConverter(uint32_t format) {
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ return &DoCopy;
+
+ case HAL_PIXEL_FORMAT_YV12:
+ return &ConvertFromYV12;
+
+ // Unsupported formats
+ // TODO(jemoreira): Conversion from these formats should be implemented as
+ // we find evidence of its usage.
+ // case HAL_PIXEL_FORMAT_BGRA_8888:
+
+ // case HAL_PIXEL_FORMAT_RGB_888:
+ // case HAL_PIXEL_FORMAT_RGB_565:
+
+ // case HAL_PIXEL_FORMAT_sRGB_A_8888:
+ // case HAL_PIXEL_FORMAT_sRGB_X_8888:
+
+ // case HAL_PIXEL_FORMAT_Y8:
+ // case HAL_PIXEL_FORMAT_Y16:
+
+ // case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ // case HAL_PIXEL_FORMAT_BLOB:
+
+ // case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ // case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ // case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ // case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ default:
+ ALOGW("Unsupported format: 0x%04x, returning null converter function",
+ format);
+ }
+ return NULL;
+}
+
+// Whether we support a given format
+bool IsFormatSupported(uint32_t format) { return GetConverter(format) != NULL; }
+
+bool CanCompositeLayer(const vsoc_hwc_layer& layer) {
+ if (layer.handle == NULL) {
+ ALOGW("%s received a layer with a null handler", __FUNCTION__);
+ return false;
+ }
+ int format = reinterpret_cast<const private_handle_t*>(layer.handle)->format;
+ if (!IsFormatSupported(format)) {
+ ALOGD("Unsupported pixel format: 0x%x, doing software composition instead",
+ format);
+ return false;
+ }
+ return true;
+}
+
+/*******************************************************************************
+Libyuv's convert functions only allow the combination of any rotation (multiple
+of 90 degrees) and a vertical flip, but not horizontal flips.
+Surfaceflinger's transformations are expressed in terms of a vertical flip, a
+horizontal flip and/or a single 90 degrees clockwise rotation (see
+NATIVE_WINDOW_TRANSFORM_HINT documentation on system/window.h for more insight).
+The following code allows to turn a horizontal flip into a 180 degrees rotation
+and a vertical flip.
+*******************************************************************************/
+libyuv::RotationMode GetRotationFromTransform(uint32_t transform) {
+ uint32_t rotation =
+ (transform & HAL_TRANSFORM_ROT_90) ? 1 : 0; // 1 * ROT90 bit
+ rotation += (transform & HAL_TRANSFORM_FLIP_H) ? 2 : 0; // 2 * VFLIP bit
+ return static_cast<libyuv::RotationMode>(90 * rotation);
+}
+
+bool GetVFlipFromTransform(uint32_t transform) {
+ // vertical flip xor horizontal flip
+ return ((transform & HAL_TRANSFORM_FLIP_V) >> 1) ^
+ (transform & HAL_TRANSFORM_FLIP_H);
+}
+
+struct BufferSpec {
+ uint8_t* buffer;
+ size_t size;
+ int width;
+ int height;
+ int stride;
+ int crop_x;
+ int crop_y;
+ int crop_width;
+ int crop_height;
+ uint32_t format;
+
+ BufferSpec(uint8_t* buffer, size_t size, int width, int height, int stride)
+ : buffer(buffer),
+ size(size),
+ width(width),
+ height(height),
+ stride(stride),
+ crop_x(0),
+ crop_y(0),
+ crop_width(width),
+ crop_height(height),
+ format(HAL_PIXEL_FORMAT_RGBA_8888) {}
+};
+
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ // use the stride in pixels as the source width
+ int stride_in_pixels = src.stride / formatToBytesPerPixel(src.format);
+
+ // The following calculation of plane offsets and alignments are based on
+ // swiftshader's Sampler::setTextureLevel() implementation
+ // (Renderer/Sampler.cpp:225)
+ uint8_t* src_y = src.buffer;
+ int stride_y = stride_in_pixels;
+ uint8_t* src_v = src_y + stride_y * src.height;
+ int stride_v = cvd::AlignToPowerOf2(stride_y / 2, 4);
+ uint8_t* src_u = src_v + stride_v * src.height / 2;
+ int stride_u = cvd::AlignToPowerOf2(stride_y / 2, 4);
+
+ // Adjust for crop
+ src_y += src.crop_y * stride_y + src.crop_x;
+ src_v += (src.crop_y / 2) * stride_v + (src.crop_x / 2);
+ src_u += (src.crop_y / 2) * stride_u + (src.crop_x / 2);
+ uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+ dst.crop_x * formatToBytesPerPixel(dst.format);
+
+ // YV12 is the same as I420, with the U and V planes swapped
+ return libyuv::I420ToARGB(src_y, stride_y, src_v, stride_v, src_u, stride_u,
+ dst_buffer, dst.stride, dst.crop_width,
+ v_flip ? -dst.crop_height : dst.crop_height);
+}
+
+int DoConversion(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ return (*GetConverter(src.format))(src, dst, v_flip);
+}
+
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ // Point to the upper left corner of the crop rectangle
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+ dst.crop_x * formatToBytesPerPixel(dst.format);
+ int width = src.crop_width;
+ int height = src.crop_height;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ // HAL formats are named based on the order of the pixel componets on the
+ // byte stream, while libyuv formats are named based on the order of those
+ // pixel components in an integer written from left to right. So
+ // libyuv::FOURCC_ARGB is equivalent to HAL_PIXEL_FORMAT_BGRA_8888.
+ return libyuv::ARGBCopy(src_buffer, src.stride, dst_buffer, dst.stride, width,
+ height);
+}
+
+int DoRotation(const BufferSpec& src, const BufferSpec& dst,
+ libyuv::RotationMode rotation, bool v_flip) {
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+ dst.crop_x * formatToBytesPerPixel(dst.format);
+ int width = src.crop_width;
+ int height = src.crop_height;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ return libyuv::ARGBRotate(src_buffer, src.stride, dst_buffer, dst.stride,
+ width, height, rotation);
+}
+
+int DoScaling(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+ dst.crop_x * formatToBytesPerPixel(dst.format);
+ int src_width = src.crop_width;
+ int src_height = src.crop_height;
+ int dst_width = dst.crop_width;
+ int dst_height = dst.crop_height;
+
+ if (v_flip) {
+ src_height = -src_height;
+ }
+
+ return libyuv::ARGBScale(src_buffer, src.stride, src_width, src_height,
+ dst_buffer, dst.stride, dst_width, dst_height,
+ libyuv::kFilterBilinear);
+}
+
+int DoAttenuation(const BufferSpec& src, const BufferSpec& dest, bool v_flip) {
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dest.buffer + dest.crop_y * dest.stride +
+ dest.crop_x * formatToBytesPerPixel(dest.format);
+ int width = dest.crop_width;
+ int height = dest.crop_height;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ return libyuv::ARGBAttenuate(src_buffer, src.stride, dst_buffer, dest.stride,
+ width, height);
+}
+
+int DoBlending(const BufferSpec& src, const BufferSpec& dest, bool v_flip) {
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dest.buffer + dest.crop_y * dest.stride +
+ dest.crop_x * formatToBytesPerPixel(dest.format);
+ int width = dest.crop_width;
+ int height = dest.crop_height;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ // libyuv's ARGB format is hwcomposer's BGRA format, since blending only cares
+ // for the position of alpha in the pixel and not the position of the colors
+ // this function is perfectly usable.
+ return libyuv::ARGBBlend(src_buffer, src.stride, dst_buffer, dest.stride,
+ dst_buffer, dest.stride, width, height);
+}
+
+} // namespace
+
+void VSoCComposer::CompositeLayer(vsoc_hwc_layer* src_layer,
+ int buffer_idx) {
+ libyuv::RotationMode rotation =
+ GetRotationFromTransform(src_layer->transform);
+
+ const private_handle_t* src_priv_handle =
+ reinterpret_cast<const private_handle_t*>(src_layer->handle);
+
+ // TODO(jemoreira): Remove the hardcoded fomat.
+ bool needs_conversion = src_priv_handle->format != HAL_PIXEL_FORMAT_RGBX_8888;
+ bool needs_scaling = LayerNeedsScaling(*src_layer);
+ bool needs_rotation = rotation != libyuv::kRotate0;
+ bool needs_transpose = needs_rotation && rotation != libyuv::kRotate180;
+ bool needs_vflip = GetVFlipFromTransform(src_layer->transform);
+ bool needs_attenuation = LayerNeedsAttenuation(*src_layer);
+ bool needs_blending = LayerNeedsBlending(*src_layer);
+ bool needs_copy = !(needs_conversion || needs_scaling || needs_rotation ||
+ needs_vflip || needs_attenuation || needs_blending);
+
+ uint8_t* src_buffer;
+ uint8_t* dst_buffer = reinterpret_cast<uint8_t*>(
+ frame_buffer_.GetBuffer(buffer_idx));
+ int retval = gralloc_module_->lock(
+ gralloc_module_, src_layer->handle, GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
+ src_priv_handle->x_res, src_priv_handle->y_res,
+ reinterpret_cast<void**>(&src_buffer));
+ if (retval) {
+ ALOGE("Got error code %d from lock function", retval);
+ return;
+ }
+ if (retval) {
+ ALOGE("Got error code %d from lock function", retval);
+ // TODO(jemoreira): Use a lock_guard-like object.
+ gralloc_module_->unlock(gralloc_module_, src_priv_handle);
+ return;
+ }
+
+ BufferSpec src_layer_spec(src_buffer, src_priv_handle->total_size,
+ src_priv_handle->x_res, src_priv_handle->y_res,
+ src_priv_handle->stride_in_pixels *
+ formatToBytesPerPixel(src_priv_handle->format));
+ src_layer_spec.crop_x = src_layer->sourceCrop.left;
+ src_layer_spec.crop_y = src_layer->sourceCrop.top;
+ src_layer_spec.crop_width =
+ src_layer->sourceCrop.right - src_layer->sourceCrop.left;
+ src_layer_spec.crop_height =
+ src_layer->sourceCrop.bottom - src_layer->sourceCrop.top;
+ src_layer_spec.format = src_priv_handle->format;
+
+ BufferSpec dst_layer_spec(dst_buffer, frame_buffer_.buffer_size(),
+ frame_buffer_.x_res(), frame_buffer_.y_res(),
+ frame_buffer_.line_length());
+ dst_layer_spec.crop_x = src_layer->displayFrame.left;
+ dst_layer_spec.crop_y = src_layer->displayFrame.top;
+ dst_layer_spec.crop_width =
+ src_layer->displayFrame.right - src_layer->displayFrame.left;
+ dst_layer_spec.crop_height =
+ src_layer->displayFrame.bottom - src_layer->displayFrame.top;
+ // TODO(jemoreira): Remove the hardcoded fomat.
+ dst_layer_spec.format = HAL_PIXEL_FORMAT_RGBX_8888;
+
+ // Add the destination layer to the bottom of the buffer stack
+ std::vector<BufferSpec> dest_buffer_stack(1, dst_layer_spec);
+
+ // If more than operation is to be performed, a temporary buffer is needed for
+ // each additional operation
+
+ // N operations need N destination buffers, the destination layer (the
+ // framebuffer) is one of them, so only N-1 temporary buffers are needed.
+ // Vertical flip is not taken into account because it can be done together
+ // with any other operation.
+ int needed_tmp_buffers = (needs_conversion ? 1 : 0) +
+ (needs_scaling ? 1 : 0) + (needs_rotation ? 1 : 0) +
+ (needs_attenuation ? 1 : 0) +
+ (needs_blending ? 1 : 0) + (needs_copy ? 1 : 0) - 1;
+
+ int x_res = src_layer->displayFrame.right - src_layer->displayFrame.left;
+ int y_res = src_layer->displayFrame.bottom - src_layer->displayFrame.top;
+ size_t output_frame_size =
+ x_res *
+ cvd::AlignToPowerOf2(y_res * frame_buffer_.bytes_per_pixel(), 4);
+ while (needed_tmp_buffers > 0) {
+ BufferSpec tmp(RotateTmpBuffer(needed_tmp_buffers), output_frame_size,
+ x_res, y_res,
+ cvd::AlignToPowerOf2(
+ x_res * frame_buffer_.bytes_per_pixel(), 4));
+ dest_buffer_stack.push_back(tmp);
+ needed_tmp_buffers--;
+ }
+
+ // Conversion and scaling should always be the first operations, so that every
+ // other operation works on equally sized frames (garanteed to fit in the tmp
+ // buffers)
+
+ // TODO(jemoreira): We are converting to ARGB as the first step under the
+ // assumption that scaling ARGB is faster than scaling I420 (the most common).
+ // This should be confirmed with testing.
+ if (needs_conversion) {
+ BufferSpec& dst_buffer_spec = dest_buffer_stack.back();
+ if (needs_scaling || needs_transpose) {
+ // If a rotation or a scaling operation are needed the dimensions at the
+ // top of the buffer stack are wrong (wrong sizes for scaling, swapped
+ // width and height for 90 and 270 rotations).
+ // Make width and height match the crop sizes on the source
+ int src_width = src_layer_spec.crop_width;
+ int src_height = src_layer_spec.crop_height;
+ int dst_stride = cvd::AlignToPowerOf2(
+ src_width * frame_buffer_.bytes_per_pixel(), 4);
+ size_t needed_size = dst_stride * src_height;
+ dst_buffer_spec.width = src_width;
+ dst_buffer_spec.height = src_height;
+ // Ajust the stride accordingly
+ dst_buffer_spec.stride = dst_stride;
+ // Crop sizes also need to be adjusted
+ dst_buffer_spec.crop_width = src_width;
+ dst_buffer_spec.crop_height = src_height;
+ dst_buffer_spec.size = needed_size;
+ // crop_x and y are fine at 0, format is already set to match destination
+
+ // In case of a scale, the source frame may be bigger than the default tmp
+ // buffer size
+ if (needed_size > tmp_buffer_.size() / kNumTmpBufferPieces) {
+ dst_buffer_spec.buffer = GetSpecialTmpBuffer(needed_size);
+ }
+ }
+ retval = DoConversion(src_layer_spec, dst_buffer_spec, needs_vflip);
+ if (retval) {
+ ALOGE("Got error code %d from DoConversion function", retval);
+ }
+ needs_vflip = false;
+ src_layer_spec = dst_buffer_spec;
+ dest_buffer_stack.pop_back();
+ }
+
+ if (needs_scaling) {
+ BufferSpec& dst_buffer_spec = dest_buffer_stack.back();
+ if (needs_transpose) {
+ // If a rotation is needed, the temporary buffer has the correct size but
+ // needs to be transposed and have its stride updated accordingly. The
+ // crop sizes also needs to be transposed, but not the x and y since they
+ // are both zero in a temporary buffer (and it is a temporary buffer
+ // because a rotation will be performed next).
+ std::swap(dst_buffer_spec.width, dst_buffer_spec.height);
+ std::swap(dst_buffer_spec.crop_width, dst_buffer_spec.crop_height);
+ // TODO (jemoreira): Aligment (To align here may cause the needed size to
+ // be bigger than the buffer, so care should be taken)
+ dst_buffer_spec.stride =
+ dst_buffer_spec.width * frame_buffer_.bytes_per_pixel();
+ }
+ retval = DoScaling(src_layer_spec, dst_buffer_spec, needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoScaling function", retval);
+ }
+ src_layer_spec = dst_buffer_spec;
+ dest_buffer_stack.pop_back();
+ }
+
+ if (needs_rotation) {
+ retval = DoRotation(src_layer_spec, dest_buffer_stack.back(), rotation,
+ needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoTransform function", retval);
+ }
+ src_layer_spec = dest_buffer_stack.back();
+ dest_buffer_stack.pop_back();
+ }
+
+ if (needs_attenuation) {
+ retval =
+ DoAttenuation(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoBlending function", retval);
+ }
+ src_layer_spec = dest_buffer_stack.back();
+ dest_buffer_stack.pop_back();
+ }
+
+ if (needs_copy) {
+ retval = DoCopy(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoBlending function", retval);
+ }
+ src_layer_spec = dest_buffer_stack.back();
+ dest_buffer_stack.pop_back();
+ }
+
+ // Blending (if needed) should always be the last operation, so that it reads
+ // and writes in the destination layer and not some temporary buffer.
+ if (needs_blending) {
+ retval = DoBlending(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoBlending function", retval);
+ }
+ // Don't need to assign destination to source in the last one
+ dest_buffer_stack.pop_back();
+ }
+
+ gralloc_module_->unlock(gralloc_module_, src_priv_handle);
+}
+
+/* static */ const int VSoCComposer::kNumTmpBufferPieces = 2;
+
+VSoCComposer::VSoCComposer(int64_t vsync_base_timestamp)
+ : BaseComposer(vsync_base_timestamp),
+ tmp_buffer_(kNumTmpBufferPieces *
+ frame_buffer_.buffer_size()) {}
+
+VSoCComposer::~VSoCComposer() {}
+
+int VSoCComposer::PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+ int composited_layers_count = 0;
+
+ // Loop over layers in inverse order of z-index
+ for (size_t layer_index = num_layers; layer_index > 0;) {
+ // Decrement here to be able to compare unsigned integer with 0 in the
+ // loop condition
+ --layer_index;
+ if (IS_TARGET_FRAMEBUFFER(layers[layer_index].compositionType)) {
+ continue;
+ }
+ if (layers[layer_index].flags & HWC_SKIP_LAYER) {
+ continue;
+ }
+ if (layers[layer_index].compositionType == HWC_BACKGROUND) {
+ layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+ continue;
+ }
+ layers[layer_index].compositionType = HWC_OVERLAY;
+ // Hwcomposer cannot draw below software-composed layers, so we need
+ // to mark those HWC_FRAMEBUFFER as well.
+ for (size_t top_idx = layer_index + 1; top_idx < num_layers; ++top_idx) {
+ // layers marked as skip are in a state that makes them unreliable to
+ // read, so it's best to assume they cover the whole screen
+ if (layers[top_idx].flags & HWC_SKIP_LAYER ||
+ (layers[top_idx].compositionType == HWC_FRAMEBUFFER &&
+ LayersOverlap(layers[layer_index], layers[top_idx]))) {
+ layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+ break;
+ }
+ }
+ if (layers[layer_index].compositionType == HWC_OVERLAY &&
+ !CanCompositeLayer(layers[layer_index])) {
+ layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+ }
+ if (layers[layer_index].compositionType == HWC_OVERLAY) {
+ ++composited_layers_count;
+ }
+ }
+ return composited_layers_count;
+}
+
+int VSoCComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+ int targetFbs = 0;
+ int buffer_idx = frame_buffer_.NextScreenBuffer();
+
+ // The framebuffer target layer should be composed if at least one layers was
+ // marked HWC_FRAMEBUFFER or if it's the only layer in the composition
+ // (unlikely)
+ bool fb_target = true;
+ for (size_t idx = 0; idx < num_layers; idx++) {
+ if (layers[idx].compositionType == HWC_FRAMEBUFFER) {
+ // At least one was found
+ fb_target = true;
+ break;
+ }
+ if (layers[idx].compositionType == HWC_OVERLAY) {
+ // Not the only layer in the composition
+ fb_target = false;
+ }
+ }
+
+ // When the framebuffer target needs to be composed, it has to go first.
+ if (fb_target) {
+ for (size_t idx = 0; idx < num_layers; idx++) {
+ if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+ CompositeLayer(&layers[idx], buffer_idx);
+ break;
+ }
+ }
+ }
+
+ for (size_t idx = 0; idx < num_layers; idx++) {
+ if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+ ++targetFbs;
+ }
+ if (layers[idx].compositionType == HWC_OVERLAY &&
+ !(layers[idx].flags & HWC_SKIP_LAYER)) {
+ CompositeLayer(&layers[idx], buffer_idx);
+ }
+ }
+ if (targetFbs != 1) {
+ ALOGW("Saw %zu layers, posted=%d", num_layers, targetFbs);
+ }
+ frame_buffer_.Broadcast(buffer_idx);
+ return 0;
+}
+
+uint8_t* VSoCComposer::RotateTmpBuffer(unsigned int order) {
+ return &tmp_buffer_[(order % kNumTmpBufferPieces) * tmp_buffer_.size() /
+ kNumTmpBufferPieces];
+}
+
+uint8_t* VSoCComposer::GetSpecialTmpBuffer(size_t needed_size) {
+ special_tmp_buffer_.resize(needed_size);
+ return &special_tmp_buffer_[0];
+}
+
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
new file mode 100644
index 0000000..7007931
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
@@ -0,0 +1,48 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <hardware/gralloc.h>
+
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+
+#include "base_composer.h"
+#include "hwcomposer.h"
+
+namespace cvd {
+
+class VSoCComposer : public BaseComposer {
+ public:
+ VSoCComposer(int64_t vsync_base_timestamp);
+ ~VSoCComposer();
+
+ // override
+ int PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers);
+ // override
+ int SetLayers(size_t num_layers, vsoc_hwc_layer* layers);
+
+ protected:
+ static const int kNumTmpBufferPieces;
+ uint8_t* RotateTmpBuffer(unsigned int order);
+ uint8_t* GetSpecialTmpBuffer(size_t needed_size);
+ void CompositeLayer(vsoc_hwc_layer* src_layer, int32_t fb_offset);
+ std::vector<uint8_t> tmp_buffer_;
+ std::vector<uint8_t> special_tmp_buffer_;
+};
+
+} // namespace cvd
diff --git a/host/commands/launch/flags.cc b/host/commands/launch/flags.cc
index 6266b07..3f62a50 100644
--- a/host/commands/launch/flags.cc
+++ b/host/commands/launch/flags.cc
@@ -186,6 +186,8 @@
"guest to host. One of [serial, vsock]");
DEFINE_int32(logcat_vsock_port, vsoc::GetPerInstanceDefault(5620),
"The port for logcat over vsock");
+DEFINE_int32(frames_vsock_port, vsoc::GetPerInstanceDefault(5580),
+ "The vsock port to receive frames from the guest on");
namespace {
template<typename S, typename T>
@@ -420,6 +422,11 @@
tmp_config_obj.set_logcat_mode(FLAGS_logcat_mode);
tmp_config_obj.set_logcat_vsock_port(FLAGS_logcat_vsock_port);
+ tmp_config_obj.set_frames_vsock_port(FLAGS_frames_vsock_port);
+ if (!tmp_config_obj.enable_ivserver()) {
+ tmp_config_obj.add_kernel_cmdline(concat("androidboot.vsock_frames_port=",
+ FLAGS_frames_vsock_port));
+ }
tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
@@ -494,8 +501,6 @@
google::FlagSettingMode::SET_FLAGS_DEFAULT);
SetCommandLineOptionWithMode("run_e2e_test", "false",
google::FlagSettingMode::SET_FLAGS_DEFAULT);
- SetCommandLineOptionWithMode("start_vnc_server", "false",
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatVsockMode,
google::FlagSettingMode::SET_FLAGS_DEFAULT);
}
diff --git a/host/commands/launch/launch.cc b/host/commands/launch/launch.cc
index 8ef31c6..766efb0 100644
--- a/host/commands/launch/launch.cc
+++ b/host/commands/launch/launch.cc
@@ -189,6 +189,16 @@
GetOnSubprocessExitCallback(config));
}
+cvd::SharedFD CreateVncInputServer(const std::string& path) {
+ auto server = cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
+ if (!server->IsOpen()) {
+ LOG(ERROR) << "Unable to create mouse server: "
+ << server->StrError();
+ return cvd::SharedFD();
+ }
+ return server;
+}
+
void LaunchVNCServerIfEnabled(const vsoc::CuttlefishConfig& config,
cvd::ProcessMonitor* process_monitor,
std::function<bool(MonitorEntry*)> callback) {
@@ -197,6 +207,33 @@
auto port_options = "-port=" + std::to_string(config.vnc_server_port());
cvd::Command vnc_server(config.vnc_server_binary());
vnc_server.AddParameter(port_options);
+ if (!config.enable_ivserver()) {
+ // When the ivserver is not enabled, the vnc touch_server needs to serve
+ // on unix sockets and send input events to whoever connects to it (namely
+ // crosvm)
+ auto touch_server = CreateVncInputServer(config.touch_socket_path());
+ if (!touch_server->IsOpen()) {
+ return;
+ }
+ vnc_server.AddParameter("-touch_fd=", touch_server);
+
+ auto keyboard_server =
+ CreateVncInputServer(config.keyboard_socket_path());
+ if (!keyboard_server->IsOpen()) {
+ return;
+ }
+ vnc_server.AddParameter("-keyboard_fd=", keyboard_server);
+ // TODO(b/128852363): This should be handled through the wayland mock
+ // instead.
+ // Additionally it receives the frame updates from a virtual socket
+ // instead
+ auto frames_server =
+ cvd::SharedFD::VsockServer(config.frames_vsock_port(), SOCK_STREAM);
+ if (!frames_server->IsOpen()) {
+ return;
+ }
+ vnc_server.AddParameter("-frame_server_fd=", frames_server);
+ }
process_monitor->StartSubprocess(std::move(vnc_server), callback);
}
}
diff --git a/host/frontend/vnc_server/Android.bp b/host/frontend/vnc_server/Android.bp
index 241df83..fae5a17 100644
--- a/host/frontend/vnc_server/Android.bp
+++ b/host/frontend/vnc_server/Android.bp
@@ -20,6 +20,7 @@
"frame_buffer_watcher.cpp",
"jpeg_compressor.cpp",
"main.cpp",
+ "screen_connector.cpp",
"simulated_hw_composer.cpp",
"virtual_inputs.cpp",
"vnc_client_connection.cpp",
diff --git a/host/frontend/vnc_server/screen_connector.cpp b/host/frontend/vnc_server/screen_connector.cpp
new file mode 100644
index 0000000..ea82f7e
--- /dev/null
+++ b/host/frontend/vnc_server/screen_connector.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/frontend/vnc_server/screen_connector.h"
+
+#include <atomic>
+#include <condition_variable>
+
+#include <gflags/gflags.h>
+
+#include <common/vsoc/lib/screen_region_view.h>
+#include <host/libs/config/cuttlefish_config.h>
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+DEFINE_int32(frame_server_fd, -1, "");
+
+namespace cvd {
+namespace vnc {
+
+namespace {
+class VSoCScreenConnector : public ScreenConnector {
+ public:
+ int WaitForNewFrameSince(std::uint32_t* seq_num) override {
+ if (!screen_view_) return -1;
+ return screen_view_->WaitForNewFrameSince(seq_num);
+ }
+
+ void* GetBuffer(int buffer_idx) override {
+ if (!screen_view_) return nullptr;
+ return screen_view_->GetBuffer(buffer_idx);
+ }
+
+ private:
+ vsoc::screen::ScreenRegionView* screen_view_ =
+ vsoc::screen::ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
+};
+
+// TODO(b/128852363): Substitute with one based on memory shared with the
+// wayland mock
+class SocketBasedScreenConnector : public ScreenConnector {
+ public:
+ SocketBasedScreenConnector(vsoc::CuttlefishConfig* config) : config_(config) {
+ screen_server_thread_ = std::thread([this]() { ServerLoop(); });
+ }
+
+ int WaitForNewFrameSince(std::uint32_t* seq_num) override {
+ std::unique_lock<std::mutex> lock(new_frame_mtx_);
+ while (seq_num_ == *seq_num) {
+ new_frame_cond_var_.wait(lock);
+ }
+ return newest_buffer_;
+ }
+
+ void* GetBuffer(int buffer_idx) override {
+ if (buffer_idx < 0) return nullptr;
+ buffer_idx %= NUM_BUFFERS_;
+ return &buffer_[buffer_idx * ScreenSizeInBytes()];
+ }
+
+ private:
+ static constexpr int NUM_BUFFERS_ = 4;
+
+ void ServerLoop() {
+ if (FLAGS_frame_server_fd < 0) {
+ LOG(FATAL) << "Invalid file descriptor: " << FLAGS_frame_server_fd;
+ return;
+ }
+ auto server = SharedFD::Dup(FLAGS_frame_server_fd);
+ close(FLAGS_frame_server_fd);
+ if (!server->IsOpen()) {
+ LOG(FATAL) << "Unable to dup screen server: " << server->StrError();
+ return;
+ }
+
+ int current_buffer = 0;
+
+ while (1) {
+ auto conn = SharedFD::Accept(*server);
+ while (conn->IsOpen()) {
+ SendScreenParameters(conn);
+
+ int32_t size = 0;
+ conn->Read(&size, sizeof(size));
+ auto buff = reinterpret_cast<uint8_t*>(GetBuffer(current_buffer));
+ while (size > 0) {
+ auto read = conn->Read(buff, size);
+ if (read < 0) {
+ LOG(ERROR) << "Failed to read from hwcomposer: "
+ << conn->StrError();
+ return;
+ }
+ size -= read;
+ buff += read;
+ }
+ BroadcastNewFrame(current_buffer);
+ current_buffer = (current_buffer + 1) % NUM_BUFFERS_;
+ }
+ }
+ }
+
+ void SendScreenParameters(SharedFD conn) const {
+ // TODO(b/128842613): Send this info from the configuration server
+ int32_t screen_params[4];
+ screen_params[0] = config_->x_res();
+ screen_params[1] = config_->y_res();
+ screen_params[2] = config_->dpi();
+ screen_params[3] = config_->refresh_rate_hz();
+ int buff_size = sizeof(screen_params);
+ int res = conn->Write(screen_params, buff_size);
+ if (res != buff_size) {
+ LOG(FATAL)
+ << "Unable to send full screen parameters to the hwcomposer ("
+ << res << "): " << conn->StrError();
+ }
+ }
+
+ void BroadcastNewFrame(int buffer_idx) {
+ {
+ std::lock_guard<std::mutex> lock(new_frame_mtx_);
+ seq_num_++;
+ newest_buffer_ = buffer_idx;
+ }
+ new_frame_cond_var_.notify_all();
+ }
+
+ vsoc::CuttlefishConfig* config_;
+ std::vector<std::uint8_t> buffer_ =
+ std::vector<std::uint8_t>(NUM_BUFFERS_ * ScreenSizeInBytes());
+ std::uint32_t seq_num_{0};
+ int newest_buffer_ = 0;
+ std::condition_variable new_frame_cond_var_;
+ std::mutex new_frame_mtx_;
+ std::thread screen_server_thread_;
+};
+} // namespace
+
+ScreenConnector* ScreenConnector::Get() {
+ auto config = vsoc::CuttlefishConfig::Get();
+ if (config->enable_ivserver()) {
+ return new VSoCScreenConnector();
+ } else {
+ return new SocketBasedScreenConnector(config);
+ }
+}
+
+} // namespace vnc
+} // namespace cvd
\ No newline at end of file
diff --git a/host/frontend/vnc_server/screen_connector.h b/host/frontend/vnc_server/screen_connector.h
new file mode 100644
index 0000000..9c7b9e0
--- /dev/null
+++ b/host/frontend/vnc_server/screen_connector.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace cvd {
+namespace vnc {
+
+class ScreenConnector {
+ public:
+ static ScreenConnector* Get();
+
+ virtual ~ScreenConnector() = default;
+
+ virtual int WaitForNewFrameSince(std::uint32_t* seq_num) = 0;
+ virtual void* GetBuffer(int buffer_idx) = 0;
+
+ protected:
+ ScreenConnector() = default;
+};
+
+} // namespace vnc
+} // namespace cvd
\ No newline at end of file
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index c8b7f03..f7061ef 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -18,7 +18,6 @@
#include "host/frontend/vnc_server/vnc_utils.h"
#include "host/libs/config/cuttlefish_config.h"
-#include "common/vsoc/lib/screen_region_view.h"
using cvd::vnc::SimulatedHWComposer;
using vsoc::screen::ScreenRegionView;
@@ -74,12 +73,11 @@
auto screen_height = ActualScreenHeight();
Message raw_screen;
std::uint64_t stripe_seq_num = 1;
- auto screen_view = ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
while (!closed()) {
bb_->WaitForAtLeastOneClientConnection();
- int buffer_idx = screen_view->WaitForNewFrameSince(&previous_seq_num);
+ int buffer_idx = screen_connector_->WaitForNewFrameSince(&previous_seq_num);
const char* frame_start =
- static_cast<char*>(screen_view->GetBuffer(buffer_idx));
+ static_cast<char*>(screen_connector_->GetBuffer(buffer_idx));
raw_screen.assign(frame_start, frame_start + ScreenSizeInBytes());
for (int i = 0; i < kNumStripes; ++i) {
diff --git a/host/frontend/vnc_server/simulated_hw_composer.h b/host/frontend/vnc_server/simulated_hw_composer.h
index 5d6e3e2..802cc7f 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.h
+++ b/host/frontend/vnc_server/simulated_hw_composer.h
@@ -26,6 +26,7 @@
#include "common/libs/thread_safe_queue/thread_safe_queue.h"
#include "common/libs/threads/thread_annotations.h"
#include "host/frontend/vnc_server/blackboard.h"
+#include "host/frontend/vnc_server/screen_connector.h"
namespace cvd {
namespace vnc {
@@ -59,6 +60,7 @@
BlackBoard* bb_{};
ThreadSafeQueue<Stripe> stripes_;
std::thread stripe_maker_;
+ std::shared_ptr<ScreenConnector> screen_connector_{ScreenConnector::Get()};
};
} // namespace vnc
} // namespace cvd
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
index b2f519c..e7e3757 100644
--- a/host/frontend/vnc_server/virtual_inputs.cpp
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -22,11 +22,20 @@
#include <mutex>
#include "keysyms.h"
+#include <common/libs/fs/shared_select.h>
+#include <host/libs/config/cuttlefish_config.h>
+
using cvd::vnc::VirtualInputs;
using vsoc::input_events::InputEventsRegionView;
+DEFINE_int32(touch_fd, -1,
+ "A fd for a socket where to accept touch connections");
+
+DEFINE_int32(keyboard_fd, -1,
+ "A fd for a socket where to accept keyboard connections");
+
namespace {
-void AddKeyMappings(std::map<uint32_t, uint32_t>* key_mapping) {
+void AddKeyMappings(std::map<uint32_t, uint16_t>* key_mapping) {
(*key_mapping)[cvd::xk::AltLeft] = KEY_LEFTALT;
(*key_mapping)[cvd::xk::ControlLeft] = KEY_LEFTCTRL;
(*key_mapping)[cvd::xk::ShiftLeft] = KEY_LEFTSHIFT;
@@ -214,30 +223,132 @@
(*key_mapping)[cvd::xk::Menu] = KEY_MENU;
(*key_mapping)[cvd::xk::VNCMenu] = KEY_MENU;
}
+
+void InitInputEvent(struct input_event* evt, uint16_t type, uint16_t code,
+ int32_t value) {
+ evt->type = type;
+ evt->code = code;
+ evt->value = value;
+}
+
} // namespace
-VirtualInputs::VirtualInputs()
- : input_events_region_view_{
- vsoc::input_events::InputEventsRegionView::GetInstance(
- vsoc::GetDomain().c_str())} {
- if (!input_events_region_view_) {
- LOG(FATAL) << "Failed to open Input events region view";
+class VSoCVirtualInputs : public VirtualInputs {
+ public:
+ VSoCVirtualInputs()
+ : input_events_region_view_{
+ vsoc::input_events::InputEventsRegionView::GetInstance(
+ vsoc::GetDomain().c_str())} {
+ if (!input_events_region_view_) {
+ LOG(FATAL) << "Failed to open Input events region view";
+ }
}
- AddKeyMappings(&keymapping_);
-}
-void VirtualInputs::GenerateKeyPressEvent(int key_code, bool down) {
- if (keymapping_.count(key_code)) {
- input_events_region_view_->HandleKeyboardEvent(down, keymapping_[key_code]);
+ void GenerateKeyPressEvent(int code, bool down) override {
+ if (keymapping_.count(code)) {
+ input_events_region_view_->HandleKeyboardEvent(down, keymapping_[code]);
+ } else {
+ LOG(ERROR) << "Unknown keycode" << code;
+ }
+ }
+
+ void PressPowerButton(bool down) override {
+ input_events_region_view_->HandlePowerButtonEvent(down);
+ }
+
+ void HandlePointerEvent(bool touch_down, int x, int y) override {
+ input_events_region_view_->HandleSingleTouchEvent(touch_down, x, y);
+ }
+
+ private:
+ vsoc::input_events::InputEventsRegionView* input_events_region_view_{};
+};
+
+class SocketVirtualInputs : public VirtualInputs {
+ public:
+ SocketVirtualInputs()
+ : client_connector_([this]() { ClientConnectorLoop(); }) {}
+
+ void GenerateKeyPressEvent(int key_code, bool down) override {
+ struct input_event events[2];
+ InitInputEvent(&events[0], EV_KEY, keymapping_[key_code], down);
+ InitInputEvent(&events[1], EV_SYN, 0, 0);
+
+ SendEvents(keyboard_socket_, events, sizeof(events));
+ }
+
+ void PressPowerButton(bool down) override {
+ struct input_event events[2];
+ InitInputEvent(&events[0], EV_KEY, KEY_POWER, down);
+ InitInputEvent(&events[1], EV_SYN, 0, 0);
+
+ SendEvents(keyboard_socket_, events, sizeof(events));
+ }
+
+ void HandlePointerEvent(bool touch_down, int x, int y) override {
+ // TODO(b/124121375): Use multitouch when available
+ struct input_event events[4];
+ InitInputEvent(&events[0], EV_ABS, ABS_X, x);
+ InitInputEvent(&events[1], EV_ABS, ABS_Y, y);
+ InitInputEvent(&events[2], EV_KEY, BTN_TOUCH, touch_down);
+ InitInputEvent(&events[3], EV_SYN, 0, 0);
+
+ SendEvents(touch_socket_, events, sizeof(events));
+ }
+
+ private:
+ void SendEvents(cvd::SharedFD socket, void* event_buffer, int byte_count) {
+ std::lock_guard<std::mutex> lock(socket_mutex_);
+ if (!socket->IsOpen()) {
+ // This is unlikely as it would only happen between the start of the vnc
+ // server and the connection of the VMM to the socket.
+ // If it happens, just drop the events as the VM is not yet ready to
+ // handle it.
+ return;
+ }
+ auto ret = socket->Write(event_buffer, byte_count);
+ if (ret < 0) {
+ LOG(ERROR) << "Error sending input event: " << socket->StrError();
+ }
+ }
+
+ void ClientConnectorLoop() {
+ auto touch_server = cvd::SharedFD::Dup(FLAGS_touch_fd);
+ close(FLAGS_touch_fd);
+ FLAGS_touch_fd = -1;
+
+ auto keyboard_server = cvd::SharedFD::Dup(FLAGS_keyboard_fd);
+ close(FLAGS_keyboard_fd);
+ FLAGS_keyboard_fd = -1;
+
+ while (1) {
+ cvd::SharedFDSet read_set;
+ read_set.Set(touch_server);
+ read_set.Set(keyboard_server);
+ cvd::Select(&read_set, nullptr, nullptr, nullptr);
+ {
+ std::lock_guard<std::mutex> lock(socket_mutex_);
+ if (read_set.IsSet(touch_server)) {
+ touch_socket_ = cvd::SharedFD::Accept(*touch_server);
+ }
+ if (read_set.IsSet(keyboard_server)) {
+ keyboard_socket_ = cvd::SharedFD::Accept(*keyboard_server);
+ }
+ }
+ }
+ }
+ cvd::SharedFD touch_socket_;
+ cvd::SharedFD keyboard_socket_;
+ std::thread client_connector_;
+ std::mutex socket_mutex_;
+};
+
+VirtualInputs::VirtualInputs() { AddKeyMappings(&keymapping_); }
+
+VirtualInputs* VirtualInputs::Get() {
+ if (vsoc::CuttlefishConfig::Get()->enable_ivserver()) {
+ return new VSoCVirtualInputs();
} else {
- LOG(INFO) << "Unknown keycode" << key_code;
+ return new SocketVirtualInputs();
}
}
-
-void VirtualInputs::PressPowerButton(bool down) {
- input_events_region_view_->HandlePowerButtonEvent(down);
-}
-
-void VirtualInputs::HandlePointerEvent(bool touch_down, int x, int y) {
- input_events_region_view_->HandleSingleTouchEvent(touch_down, x, y);
-}
diff --git a/host/frontend/vnc_server/virtual_inputs.h b/host/frontend/vnc_server/virtual_inputs.h
index c937a12..f92693b 100644
--- a/host/frontend/vnc_server/virtual_inputs.h
+++ b/host/frontend/vnc_server/virtual_inputs.h
@@ -28,15 +28,18 @@
class VirtualInputs {
public:
+ static VirtualInputs* Get();
+
+ virtual ~VirtualInputs() = default;
+
+ virtual void GenerateKeyPressEvent(int code, bool down) = 0;
+ virtual void PressPowerButton(bool down) = 0;
+ virtual void HandlePointerEvent(bool touch_down, int x, int y) = 0;
+
+ protected:
VirtualInputs();
- void GenerateKeyPressEvent(int code, bool down);
- void PressPowerButton(bool down);
- void HandlePointerEvent(bool touch_down, int x, int y);
-
- private:
- vsoc::input_events::InputEventsRegionView* input_events_region_view_{};
- std::map<uint32_t, uint32_t> keymapping_;
+ std::map<uint32_t, uint16_t> keymapping_;
};
} // namespace vnc
diff --git a/host/frontend/vnc_server/vnc_client_connection.cpp b/host/frontend/vnc_server/vnc_client_connection.cpp
index 9bed6ff..a8faf47 100644
--- a/host/frontend/vnc_server/vnc_client_connection.cpp
+++ b/host/frontend/vnc_server/vnc_client_connection.cpp
@@ -133,9 +133,9 @@
} // namespace vnc
} // namespace cvd
-VncClientConnection::VncClientConnection(ClientSocket client,
- VirtualInputs* virtual_inputs,
- BlackBoard* bb, bool aggressive)
+VncClientConnection::VncClientConnection(
+ ClientSocket client, std::shared_ptr<VirtualInputs> virtual_inputs,
+ BlackBoard* bb, bool aggressive)
: client_{std::move(client)}, virtual_inputs_{virtual_inputs}, bb_{bb} {
frame_buffer_request_handler_tid_ = std::thread(
&VncClientConnection::FrameBufferUpdateRequestHandler, this, aggressive);
diff --git a/host/frontend/vnc_server/vnc_client_connection.h b/host/frontend/vnc_server/vnc_client_connection.h
index 80aaad1..73beae1 100644
--- a/host/frontend/vnc_server/vnc_client_connection.h
+++ b/host/frontend/vnc_server/vnc_client_connection.h
@@ -35,7 +35,8 @@
class VncClientConnection {
public:
- VncClientConnection(ClientSocket client, VirtualInputs* virtual_inputs,
+ VncClientConnection(ClientSocket client,
+ std::shared_ptr<VirtualInputs> virtual_inputs,
BlackBoard* bb, bool aggressive);
VncClientConnection(const VncClientConnection&) = delete;
VncClientConnection& operator=(const VncClientConnection&) = delete;
@@ -139,7 +140,7 @@
ClientSocket client_;
bool control_key_down_ = false;
bool meta_key_down_ = false;
- VirtualInputs* virtual_inputs_{};
+ std::shared_ptr<VirtualInputs> virtual_inputs_{};
FrameBufferUpdateRequest previous_update_request_{};
BlackBoard* bb_;
diff --git a/host/frontend/vnc_server/vnc_server.cpp b/host/frontend/vnc_server/vnc_server.cpp
index 03f5dbe..3aba467 100644
--- a/host/frontend/vnc_server/vnc_server.cpp
+++ b/host/frontend/vnc_server/vnc_server.cpp
@@ -28,7 +28,10 @@
using cvd::vnc::VncServer;
VncServer::VncServer(int port, bool aggressive)
- : server_(port), frame_buffer_watcher_{&bb_}, aggressive_{aggressive} {}
+ : server_(port),
+ virtual_inputs_(VirtualInputs::Get()),
+ frame_buffer_watcher_{&bb_},
+ aggressive_{aggressive} {}
void VncServer::MainLoop() {
while (true) {
@@ -50,7 +53,7 @@
// data members. In the current setup, if the VncServer is destroyed with
// clients still running, the clients will all be left with dangling
// pointers.
- VncClientConnection client(std::move(sock), &virtual_inputs_, &bb_,
+ VncClientConnection client(std::move(sock), virtual_inputs_, &bb_,
aggressive_);
client.StartSession();
}
diff --git a/host/frontend/vnc_server/vnc_server.h b/host/frontend/vnc_server/vnc_server.h
index d88835c..66e17e0 100644
--- a/host/frontend/vnc_server/vnc_server.h
+++ b/host/frontend/vnc_server/vnc_server.h
@@ -16,6 +16,7 @@
* limitations under the License.
*/
+#include <memory>
#include <string>
#include <thread>
#include <utility>
@@ -46,7 +47,7 @@
void StartClientThread(ClientSocket sock);
ServerSocket server_;
- VirtualInputs virtual_inputs_;
+ std::shared_ptr<VirtualInputs> virtual_inputs_;
BlackBoard bb_;
FrameBufferWatcher frame_buffer_watcher_;
bool aggressive_{};
diff --git a/host/frontend/vnc_server/vnc_utils.h b/host/frontend/vnc_server/vnc_utils.h
index 3eac9f3..194e828 100644
--- a/host/frontend/vnc_server/vnc_utils.h
+++ b/host/frontend/vnc_server/vnc_utils.h
@@ -21,6 +21,7 @@
#include <utility>
#include <vector>
+#include "common/libs/utils/size_utils.h"
#include "common/libs/tcp_socket/tcp_socket.h"
#include "common/vsoc/lib/screen_region_view.h"
#include "host/libs/config/cuttlefish_config.h"
@@ -68,16 +69,12 @@
// The width of the screen regardless of orientation. Does not change.
inline int ActualScreenWidth() {
- return vsoc::screen::ScreenRegionView::GetInstance(
- vsoc::GetDomain().c_str())
- ->x_res();
+ return AlignToPowerOf2(vsoc::CuttlefishConfig::Get()->x_res(), 4);
}
// The height of the screen regardless of orientation. Does not change.
inline int ActualScreenHeight() {
- return vsoc::screen::ScreenRegionView::GetInstance(
- vsoc::GetDomain().c_str())
- ->y_res();
+ return vsoc::CuttlefishConfig::Get()->y_res();
}
inline int ScreenSizeInBytes() {
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 82ac8e3..2c4ba95 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -154,7 +154,8 @@
const char* kBlankDataImageFmt = "blank_data_image_fmt";
const char* kLogcatMode = "logcat_mode";
-const char* kLogcatVsockPort = "logcat_vsock_mode";
+const char* kLogcatVsockPort = "logcat_vsock_port";
+const char* kFramesVsockPort = "frames_vsock_port";
const char* kLogcatReceiverBinary = "logcat_receiver_binary";
} // namespace
@@ -793,6 +794,14 @@
return (*dictionary_)[kLogcatVsockPort].asInt();
}
+void CuttlefishConfig::set_frames_vsock_port(int port) {
+ (*dictionary_)[kFramesVsockPort] = port;
+}
+
+int CuttlefishConfig::frames_vsock_port() const {
+ return (*dictionary_)[kFramesVsockPort].asInt();
+}
+
void CuttlefishConfig::set_logcat_receiver_binary(const std::string& binary) {
SetPath(kLogcatReceiverBinary, binary);
}
@@ -801,11 +810,18 @@
return (*dictionary_)[kLogcatReceiverBinary].asString();
}
-
bool CuttlefishConfig::enable_ivserver() const {
return hardware_name() == "cutf_ivsh";
}
+std::string CuttlefishConfig::touch_socket_path() const {
+ return PerInstancePath("touch.sock");
+}
+
+std::string CuttlefishConfig::keyboard_socket_path() const {
+ return PerInstancePath("keyboard.sock");
+}
+
// Creates the (initially empty) config object and populates it with values from
// the config file if the CUTTLEFISH_CONFIG_FILE env variable is present.
// Returns nullptr if there was an error loading from file
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 9f87399..75dbddc 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -302,8 +302,14 @@
void set_logcat_vsock_port(int port);
int logcat_vsock_port() const;
+ void set_frames_vsock_port(int port);
+ int frames_vsock_port() const;
+
bool enable_ivserver() const;
+ std::string touch_socket_path() const;
+ std::string keyboard_socket_path() const;
+
private:
std::unique_ptr<Json::Value> dictionary_;
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index b623157..2cee6b3 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -85,6 +85,9 @@
command.AddParameter("--rwdisk=", config_->metadata_image_path());
command.AddParameter("--socket=", GetControlSocketPath(config_));
command.AddParameter("--android-fstab=", config_->gsi_fstab_path());
+ command.AddParameter("--single-touch=", config_->touch_socket_path(), ":",
+ config_->x_res(), ":", config_->y_res());
+ command.AddParameter("--keyboard=", config_->keyboard_socket_path());
AddTapFdParameter(&command, config_->wifi_tap_name());
AddTapFdParameter(&command, config_->mobile_tap_name());