diff options
Diffstat (limited to 'host/frontend/vnc_server/vnc_client_connection.cpp')
-rw-r--r-- | host/frontend/vnc_server/vnc_client_connection.cpp | 656 |
1 files changed, 0 insertions, 656 deletions
diff --git a/host/frontend/vnc_server/vnc_client_connection.cpp b/host/frontend/vnc_server/vnc_client_connection.cpp deleted file mode 100644 index b81bb88b..00000000 --- a/host/frontend/vnc_server/vnc_client_connection.cpp +++ /dev/null @@ -1,656 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "host/frontend/vnc_server/vnc_client_connection.h" - -#include <netinet/in.h> -#include <sys/time.h> - -#include <algorithm> -#include <cmath> -#include <cstdint> -#include <cstring> -#include <memory> -#include <mutex> -#include <string> -#include <thread> -#include <utility> -#include <vector> - -#include <gflags/gflags.h> -#include <glog/logging.h> -#include "common/libs/tcp_socket/tcp_socket.h" -#include "host/frontend/vnc_server/keysyms.h" -#include "host/frontend/vnc_server/mocks.h" -#include "host/frontend/vnc_server/vnc_utils.h" -#include "host/libs/config/cuttlefish_config.h" - -using cvd::Message; -using cvd::vnc::Stripe; -using cvd::vnc::StripePtrVec; -using cvd::vnc::VncClientConnection; - -struct ScreenRegionView { - using Pixel = uint32_t; - static constexpr int kSwiftShaderPadding = 4; - static constexpr int kRedShift = 0; - static constexpr int kGreenShift = 8; - static constexpr int kBlueShift = 16; - static constexpr int kRedBits = 8; - static constexpr int kGreenBits = 8; - static constexpr int kBlueBits = 8; -}; - -DEFINE_bool(debug_client, false, "Turn on detailed logging for the client"); - -#define DLOG(LEVEL) \ - if (FLAGS_debug_client) LOG(LEVEL) - -namespace { -class BigEndianChecker { - public: - BigEndianChecker() { - uint32_t u = 1; - is_big_endian_ = *reinterpret_cast<const char*>(&u) == 0; - } - bool operator()() const { return is_big_endian_; } - - private: - bool is_big_endian_{}; -}; - -const BigEndianChecker ImBigEndian; - -constexpr int32_t kDesktopSizeEncoding = -223; -constexpr int32_t kTightEncoding = 7; - -// These are the lengths not counting the first byte. The first byte -// indicates the message type. -constexpr size_t kSetPixelFormatLength = 19; -constexpr size_t kFramebufferUpdateRequestLength = 9; -constexpr size_t kSetEncodingsLength = 3; // more bytes follow -constexpr size_t kKeyEventLength = 7; -constexpr size_t kPointerEventLength = 5; -constexpr size_t kClientCutTextLength = 7; // more bytes follow - -std::string HostName() { - auto config = vsoc::CuttlefishConfig::Get(); - return !config || config->device_title().empty() ? std::string{"localhost"} - : config->device_title(); -} - -std::uint16_t uint16_tAt(const void* p) { - std::uint16_t u{}; - std::memcpy(&u, p, sizeof u); - return ntohs(u); -} - -std::uint32_t uint32_tAt(const void* p) { - std::uint32_t u{}; - std::memcpy(&u, p, sizeof u); - return ntohl(u); -} - -std::int32_t int32_tAt(const void* p) { - std::uint32_t u{}; - std::memcpy(&u, p, sizeof u); - u = ntohl(u); - std::int32_t s{}; - std::memcpy(&s, &u, sizeof s); - return s; -} - -std::uint32_t RedVal(std::uint32_t pixel) { - return (pixel >> ScreenRegionView::kRedShift) & - ((0x1 << ScreenRegionView::kRedBits) - 1); -} - -std::uint32_t BlueVal(std::uint32_t pixel) { - return (pixel >> ScreenRegionView::kBlueShift) & - ((0x1 << ScreenRegionView::kBlueBits) - 1); -} - -std::uint32_t GreenVal(std::uint32_t pixel) { - return (pixel >> ScreenRegionView::kGreenShift) & - ((0x1 << ScreenRegionView::kGreenBits) - 1); -} -} // namespace -namespace cvd { -namespace vnc { -bool operator==(const VncClientConnection::FrameBufferUpdateRequest& lhs, - const VncClientConnection::FrameBufferUpdateRequest& rhs) { - return lhs.x_pos == rhs.x_pos && lhs.y_pos == rhs.y_pos && - lhs.width == rhs.width && lhs.height == rhs.height; -} - -bool operator!=(const VncClientConnection::FrameBufferUpdateRequest& lhs, - const VncClientConnection::FrameBufferUpdateRequest& rhs) { - return !(lhs == rhs); -} -} // namespace vnc -} // namespace cvd - -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); -} - -VncClientConnection::~VncClientConnection() { - { - std::lock_guard<std::mutex> guard(m_); - closed_ = true; - } - bb_->StopWaiting(this); - frame_buffer_request_handler_tid_.join(); -} - -void VncClientConnection::StartSession() { - LOG(INFO) << "Starting session"; - SetupProtocol(); - LOG(INFO) << "Protocol set up"; - if (client_.closed()) { - return; - } - SetupSecurityType(); - LOG(INFO) << "Security type set"; - if (client_.closed()) { - return; - } - GetClientInit(); - LOG(INFO) << "Gotten client init"; - if (client_.closed()) { - return; - } - SendServerInit(); - LOG(INFO) << "Sent server init"; - if (client_.closed()) { - return; - } - NormalSession(); - LOG(INFO) << "vnc session terminated"; -} - -bool VncClientConnection::closed() { - std::lock_guard<std::mutex> guard(m_); - return closed_; -} - -void VncClientConnection::SetupProtocol() { - static constexpr char kRFBVersion[] = "RFB 003.008\n"; - static constexpr auto kVersionLen = (sizeof kRFBVersion) - 1; - client_.SendNoSignal(reinterpret_cast<const std::uint8_t*>(kRFBVersion), - kVersionLen); - auto client_protocol = client_.Recv(kVersionLen); - if (std::memcmp(&client_protocol[0], kRFBVersion, - std::min(kVersionLen, client_protocol.size())) != 0) { - client_protocol.push_back('\0'); - LOG(ERROR) << "vnc client wants a different protocol: " - << reinterpret_cast<const char*>(&client_protocol[0]); - } -} - -void VncClientConnection::SetupSecurityType() { - static constexpr std::uint8_t kNoneSecurity = 0x1; - // The first '0x1' indicates the number of items that follow - static constexpr std::uint8_t kOnlyNoneSecurity[] = {0x01, kNoneSecurity}; - client_.SendNoSignal(kOnlyNoneSecurity); - auto client_security = client_.Recv(1); - if (client_.closed()) { - return; - } - if (client_security.front() != kNoneSecurity) { - LOG(ERROR) << "vnc client is asking for security type " - << static_cast<int>(client_security.front()); - } - static constexpr std::uint8_t kZero[4] = {}; - client_.SendNoSignal(kZero); -} - -void VncClientConnection::GetClientInit() { - auto client_shared = client_.Recv(1); -} - -void VncClientConnection::SendServerInit() { - const std::string server_name = HostName(); - std::lock_guard<std::mutex> guard(m_); - auto server_init = cvd::CreateMessage( - static_cast<std::uint16_t>(ScreenWidth()), - static_cast<std::uint16_t>(ScreenHeight()), pixel_format_.bits_per_pixel, - pixel_format_.depth, pixel_format_.big_endian, pixel_format_.true_color, - pixel_format_.red_max, pixel_format_.green_max, pixel_format_.blue_max, - pixel_format_.red_shift, pixel_format_.green_shift, - pixel_format_.blue_shift, std::uint16_t{}, // padding - std::uint8_t{}, // padding - static_cast<std::uint32_t>(server_name.size()), server_name); - client_.SendNoSignal(server_init); -} - -Message VncClientConnection::MakeFrameBufferUpdateHeader( - std::uint16_t num_stripes) { - return cvd::CreateMessage(std::uint8_t{0}, // message-type - std::uint8_t{}, // padding - std::uint16_t{num_stripes}); -} - -void VncClientConnection::AppendRawStripeHeader(Message* frame_buffer_update, - const Stripe& stripe) { - static constexpr int32_t kRawEncoding = 0; - cvd::AppendToMessage(frame_buffer_update, std::uint16_t{stripe.x}, - std::uint16_t{stripe.y}, std::uint16_t{stripe.width}, - std::uint16_t{stripe.height}, kRawEncoding); -} - -void VncClientConnection::AppendJpegSize(Message* frame_buffer_update, - size_t jpeg_size) { - constexpr size_t kJpegSizeOneByteMax = 127; - constexpr size_t kJpegSizeTwoByteMax = 16383; - constexpr size_t kJpegSizeThreeByteMax = 4194303; - - if (jpeg_size <= kJpegSizeOneByteMax) { - cvd::AppendToMessage(frame_buffer_update, - static_cast<std::uint8_t>(jpeg_size)); - } else if (jpeg_size <= kJpegSizeTwoByteMax) { - auto sz = static_cast<std::uint32_t>(jpeg_size); - cvd::AppendToMessage(frame_buffer_update, - static_cast<std::uint8_t>((sz & 0x7F) | 0x80), - static_cast<std::uint8_t>((sz >> 7) & 0xFF)); - } else { - if (jpeg_size > kJpegSizeThreeByteMax) { - LOG(FATAL) << "jpeg size is too big: " << jpeg_size << " must be under " - << kJpegSizeThreeByteMax; - } - const auto sz = static_cast<std::uint32_t>(jpeg_size); - cvd::AppendToMessage(frame_buffer_update, - static_cast<std::uint8_t>((sz & 0x7F) | 0x80), - static_cast<std::uint8_t>(((sz >> 7) & 0x7F) | 0x80), - static_cast<std::uint8_t>((sz >> 14) & 0xFF)); - } -} - -void VncClientConnection::AppendRawStripe(Message* frame_buffer_update, - const Stripe& stripe) const { - using Pixel = ScreenRegionView::Pixel; - auto& fbu = *frame_buffer_update; - AppendRawStripeHeader(&fbu, stripe); - auto init_size = fbu.size(); - fbu.insert(fbu.end(), stripe.raw_data.begin(), stripe.raw_data.end()); - for (size_t i = init_size; i < fbu.size(); i += sizeof(Pixel)) { - CHECK_LE(i + sizeof(Pixel), fbu.size()); - Pixel raw_pixel{}; - std::memcpy(&raw_pixel, &fbu[i], sizeof raw_pixel); - auto red = RedVal(raw_pixel); - auto green = GreenVal(raw_pixel); - auto blue = BlueVal(raw_pixel); - Pixel pixel = Pixel{red} << pixel_format_.red_shift | - Pixel{blue} << pixel_format_.blue_shift | - Pixel{green} << pixel_format_.green_shift; - - if (bool(pixel_format_.big_endian) != ImBigEndian()) { - // flip them bits (refactor into function) - auto p = reinterpret_cast<char*>(&pixel); - std::swap(p[0], p[3]); - std::swap(p[1], p[2]); - } - std::memcpy(&fbu[i], &pixel, sizeof pixel); - } -} - -Message VncClientConnection::MakeRawFrameBufferUpdate( - const StripePtrVec& stripes) const { - auto fbu = - MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size())); - for (auto& stripe : stripes) { - AppendRawStripe(&fbu, *stripe); - } - return fbu; -} - -void VncClientConnection::AppendJpegStripeHeader(Message* frame_buffer_update, - const Stripe& stripe) { - static constexpr std::uint8_t kJpegEncoding = 0x90; - cvd::AppendToMessage(frame_buffer_update, stripe.x, stripe.y, stripe.width, - stripe.height, kTightEncoding, kJpegEncoding); - AppendJpegSize(frame_buffer_update, stripe.jpeg_data.size()); -} - -void VncClientConnection::AppendJpegStripe(Message* frame_buffer_update, - const Stripe& stripe) { - AppendJpegStripeHeader(frame_buffer_update, stripe); - frame_buffer_update->insert(frame_buffer_update->end(), - stripe.jpeg_data.begin(), stripe.jpeg_data.end()); -} - -Message VncClientConnection::MakeJpegFrameBufferUpdate( - const StripePtrVec& stripes) { - auto fbu = - MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size())); - for (auto& stripe : stripes) { - AppendJpegStripe(&fbu, *stripe); - } - return fbu; -} - -Message VncClientConnection::MakeFrameBufferUpdate( - const StripePtrVec& stripes) { - return use_jpeg_compression_ ? MakeJpegFrameBufferUpdate(stripes) - : MakeRawFrameBufferUpdate(stripes); -} - -void VncClientConnection::FrameBufferUpdateRequestHandler(bool aggressive) { - BlackBoard::Registerer reg(bb_, this); - - while (!closed()) { - auto stripes = bb_->WaitForSenderWork(this); - if (closed()) { - break; - } - if (stripes.empty()) { - LOG(FATAL) << "Got 0 stripes"; - } - { - // lock here so a portrait frame can't be sent after a landscape - // DesktopSize update, or vice versa. - std::lock_guard<std::mutex> guard(m_); - DLOG(INFO) << "Sending update in " - << (current_orientation_ == ScreenOrientation::Portrait - ? "portrait" - : "landscape") - << " mode"; - client_.SendNoSignal(MakeFrameBufferUpdate(stripes)); - } - if (aggressive) { - bb_->FrameBufferUpdateRequestReceived(this); - } - } -} - -void VncClientConnection::SendDesktopSizeUpdate() { - static constexpr int32_t kDesktopSizeEncoding = -223; - client_.SendNoSignal(cvd::CreateMessage( - std::uint8_t{0}, // message-type, - std::uint8_t{}, // padding - std::uint16_t{1}, // one pseudo rectangle - std::uint16_t{0}, std::uint16_t{0}, - static_cast<std::uint16_t>(ScreenWidth()), - static_cast<std::uint16_t>(ScreenHeight()), kDesktopSizeEncoding)); -} - -bool VncClientConnection::IsUrgent( - const FrameBufferUpdateRequest& update_request) const { - return !update_request.incremental || - update_request != previous_update_request_; -} - -void VncClientConnection::HandleFramebufferUpdateRequest() { - auto msg = client_.Recv(kFramebufferUpdateRequestLength); - if (msg.size() != kFramebufferUpdateRequestLength) { - return; - } - FrameBufferUpdateRequest fbur{msg[1] == 0, uint16_tAt(&msg[1]), - uint16_tAt(&msg[3]), uint16_tAt(&msg[5]), - uint16_tAt(&msg[7])}; - if (IsUrgent(fbur)) { - bb_->SignalClientNeedsEntireScreen(this); - } - bb_->FrameBufferUpdateRequestReceived(this); - previous_update_request_ = fbur; -} - -void VncClientConnection::HandleSetEncodings() { - auto msg = client_.Recv(kSetEncodingsLength); - if (msg.size() != kSetEncodingsLength) { - return; - } - auto count = uint16_tAt(&msg[1]); - auto encodings = client_.Recv(count * sizeof(int32_t)); - if (encodings.size() % sizeof(int32_t) != 0) { - return; - } - { - std::lock_guard<std::mutex> guard(m_); - use_jpeg_compression_ = false; - } - for (size_t i = 0; i < encodings.size(); i += sizeof(int32_t)) { - auto enc = int32_tAt(&encodings[i]); - DLOG(INFO) << "client requesting encoding: " << enc; - if (enc == kTightEncoding) { - // This is a deviation from the spec which says that if a jpeg quality - // level is not specified, tight encoding won't use jpeg. - std::lock_guard<std::mutex> guard(m_); - use_jpeg_compression_ = true; - } - if (kJpegMinQualityEncoding <= enc && enc <= kJpegMaxQualityEncoding) { - DLOG(INFO) << "jpeg compression level: " << enc; - bb_->set_jpeg_quality_level(enc); - } - if (enc == kDesktopSizeEncoding) { - supports_desktop_size_encoding_ = true; - } - } -} - -void VncClientConnection::HandleSetPixelFormat() { - std::lock_guard<std::mutex> guard(m_); - auto msg = client_.Recv(kSetPixelFormatLength); - if (msg.size() != kSetPixelFormatLength) { - return; - } - pixel_format_.bits_per_pixel = msg[3]; - pixel_format_.depth = msg[4]; - pixel_format_.big_endian = msg[5]; - pixel_format_.true_color = msg[7]; - pixel_format_.red_max = uint16_tAt(&msg[8]); - pixel_format_.green_max = uint16_tAt(&msg[10]); - pixel_format_.blue_max = uint16_tAt(&msg[12]); - pixel_format_.red_shift = msg[13]; - pixel_format_.green_shift = msg[14]; - pixel_format_.blue_shift = msg[15]; -} - -void VncClientConnection::HandlePointerEvent() { - auto msg = client_.Recv(kPointerEventLength); - if (msg.size() != kPointerEventLength) { - return; - } - std::uint8_t button_mask = msg[0]; - auto x_pos = uint16_tAt(&msg[1]); - auto y_pos = uint16_tAt(&msg[3]); - { - std::lock_guard<std::mutex> guard(m_); - if (current_orientation_ == ScreenOrientation::Landscape) { - std::tie(x_pos, y_pos) = - std::make_pair(ActualScreenWidth() - y_pos, x_pos); - } - } - virtual_inputs_->HandlePointerEvent(button_mask, x_pos, y_pos); -} - -void VncClientConnection::UpdateAccelerometer(float /*x*/, float /*y*/, - float /*z*/) { - // TODO(jemoreira): Implement when vsoc sensor hal is updated -} - -VncClientConnection::Coordinates VncClientConnection::CoordinatesForOrientation( - ScreenOrientation orientation) const { - // Compute the acceleration vector that we need to send to mimic - // this change. - constexpr float g = 9.81; - constexpr float angle = 20.0; - const float cos_angle = std::cos(angle / M_PI); - const float sin_angle = std::sin(angle / M_PI); - const float z = g * sin_angle; - switch (orientation) { - case ScreenOrientation::Portrait: - return {0, g * cos_angle, z}; - case ScreenOrientation::Landscape: - return {g * cos_angle, 0, z}; - } -} - -int VncClientConnection::ScreenWidth() const { - return current_orientation_ == ScreenOrientation::Portrait - ? ActualScreenWidth() - : ActualScreenHeight(); -} - -int VncClientConnection::ScreenHeight() const { - return current_orientation_ == ScreenOrientation::Portrait - ? ActualScreenHeight() - : ActualScreenWidth(); -} - -void VncClientConnection::SetScreenOrientation(ScreenOrientation orientation) { - std::lock_guard<std::mutex> guard(m_); - auto coords = CoordinatesForOrientation(orientation); - UpdateAccelerometer(coords.x, coords.y, coords.z); - if (supports_desktop_size_encoding_) { - auto previous_orientation = current_orientation_; - current_orientation_ = orientation; - if (current_orientation_ != previous_orientation && - supports_desktop_size_encoding_) { - SendDesktopSizeUpdate(); - bb_->SetOrientation(this, current_orientation_); - // TODO not sure if I should be sending a frame update along with this, - // or just letting the next FBUR handle it. This seems to me like it's - // sending one more frame buffer update than was requested, which is - // maybe a violation of the spec? - } - } -} - -bool VncClientConnection::RotateIfIsRotationCommand(std::uint32_t key) { - // Due to different configurations on different platforms we're supporting - // a set of options for rotating the screen. These are similar to what - // the emulator supports and has supported. - // ctrl+left and ctrl+right work on windows and linux - // command+left and command+right work on Mac - // ctrl+fn+F11 and ctrl+fn+F12 work when chromoting to ubuntu from a Mac - if (!control_key_down_ && !meta_key_down_) { - return false; - } - switch (key) { - case cvd::xk::Right: - case cvd::xk::F12: - DLOG(INFO) << "switching to portrait"; - SetScreenOrientation(ScreenOrientation::Portrait); - break; - case cvd::xk::Left: - case cvd::xk::F11: - DLOG(INFO) << "switching to landscape"; - SetScreenOrientation(ScreenOrientation::Landscape); - break; - default: - return false; - } - return true; -} - -void VncClientConnection::HandleKeyEvent() { - auto msg = client_.Recv(kKeyEventLength); - if (msg.size() != kKeyEventLength) { - return; - } - - auto key = uint32_tAt(&msg[3]); - bool key_down = msg[0]; - switch (key) { - case cvd::xk::ControlLeft: - case cvd::xk::ControlRight: - control_key_down_ = key_down; - break; - case cvd::xk::MetaLeft: - case cvd::xk::MetaRight: - meta_key_down_ = key_down; - break; - case cvd::xk::F5: - key = cvd::xk::Menu; - break; - case cvd::xk::F7: - virtual_inputs_->PressPowerButton(key_down); - return; - default: - break; - } - - if (RotateIfIsRotationCommand(key)) { - return; - } - - virtual_inputs_->GenerateKeyPressEvent(key, key_down); -} - -void VncClientConnection::HandleClientCutText() { - auto msg = client_.Recv(kClientCutTextLength); - if (msg.size() != kClientCutTextLength) { - return; - } - auto len = uint32_tAt(&msg[3]); - client_.Recv(len); -} - -void VncClientConnection::NormalSession() { - static constexpr std::uint8_t kSetPixelFormatMessage{0}; - static constexpr std::uint8_t kSetEncodingsMessage{2}; - static constexpr std::uint8_t kFramebufferUpdateRequestMessage{3}; - static constexpr std::uint8_t kKeyEventMessage{4}; - static constexpr std::uint8_t kPointerEventMessage{5}; - static constexpr std::uint8_t kClientCutTextMessage{6}; - while (true) { - if (client_.closed()) { - return; - } - auto msg = client_.Recv(1); - if (client_.closed()) { - return; - } - auto msg_type = msg.front(); - DLOG(INFO) << "Received message type " << msg_type; - - switch (msg_type) { - case kSetPixelFormatMessage: - HandleSetPixelFormat(); - break; - - case kSetEncodingsMessage: - HandleSetEncodings(); - break; - - case kFramebufferUpdateRequestMessage: - HandleFramebufferUpdateRequest(); - break; - - case kKeyEventMessage: - HandleKeyEvent(); - break; - - case kPointerEventMessage: - HandlePointerEvent(); - break; - - case kClientCutTextMessage: - HandleClientCutText(); - break; - - default: - LOG(WARNING) << "message type not handled: " - << static_cast<int>(msg_type); - break; - } - } -} |