1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "vsock_hwc"
18 
19 #include "guest/hals/hwcomposer/cutf_cvm/vsocket_screen_view.h"
20 
21 #include <cutils/properties.h>
22 #include <log/log.h>
23 
24 #include "common/libs/device_config/device_config.h"
25 
26 namespace cvd {
27 
VsocketScreenView()28 VsocketScreenView::VsocketScreenView()
29     : broadcast_thread_([this]() { BroadcastLoop(); }) {
30   GetScreenParameters();
31   // inner_buffer needs to be initialized after the final values of the screen
32   // parameters are set (either from the config server or default).
33   inner_buffer_ = std::vector<char>(buffer_size() * 8);
34 }
35 
~VsocketScreenView()36 VsocketScreenView::~VsocketScreenView() {
37   running_ = false;
38   broadcast_thread_.join();
39 }
40 
GetScreenParameters()41 void VsocketScreenView::GetScreenParameters() {
42   auto device_config = cvd::DeviceConfig::Get();
43   if (!device_config) {
44     ALOGI(
45         "Failed to obtain device configuration from server, running in "
46         "headless mode");
47     // It is impossible to ensure host and guest agree on the screen parameters
48     // if these could not be read from the host configuration server. It's best
49     // to not attempt to send frames in this case.
50     running_ = false;
51     // Do a phony Broadcast to ensure the broadcaster thread exits.
52     Broadcast(-1);
53     return;
54   }
55   x_res_ = device_config->screen_x_res();
56   y_res_ = device_config->screen_y_res();
57   dpi_ = device_config->screen_dpi();
58   refresh_rate_ = device_config->screen_refresh_rate();
59   ALOGI("Received screen parameters: res=%dx%d, dpi=%d, freq=%d", x_res_,
60         y_res_, dpi_, refresh_rate_);
61 }
62 
ConnectToScreenServer()63 bool VsocketScreenView::ConnectToScreenServer() {
64   auto vsock_frames_port = property_get_int64("ro.boot.vsock_frames_port", -1);
65   if (vsock_frames_port <= 0) {
66     ALOGI("No screen server configured, operating on headless mode");
67     return false;
68   }
69 
70   screen_server_ = cvd::SharedFD::VsockClient(
71       2, static_cast<unsigned int>(vsock_frames_port), SOCK_STREAM);
72   if (!screen_server_->IsOpen()) {
73     ALOGE("Unable to connect to screen server: %s", screen_server_->StrError());
74     return false;
75   }
76 
77   return true;
78 }
79 
BroadcastLoop()80 void VsocketScreenView::BroadcastLoop() {
81   auto connected = ConnectToScreenServer();
82   if (!connected) {
83     ALOGE(
84         "Broadcaster thread exiting due to no connection to screen server. "
85         "Compositions will occur, but frames won't be sent anywhere");
86     return;
87   }
88   int current_seq = 0;
89   int current_offset;
90   ALOGI("Broadcaster thread loop starting");
91   while (true) {
92     {
93       std::unique_lock<std::mutex> lock(mutex_);
94       while (running_ && current_seq == current_seq_) {
95         cond_var_.wait(lock);
96       }
97       if (!running_) {
98         ALOGI("Broadcaster thread exiting");
99         return;
100       }
101       current_offset = current_offset_;
102       current_seq = current_seq_;
103     }
104     int32_t size = buffer_size();
105     screen_server_->Write(&size, sizeof(size));
106     auto buff = static_cast<char*>(GetBuffer(current_offset));
107     while (size > 0) {
108       auto written = screen_server_->Write(buff, size);
109       if (written == -1) {
110         ALOGE("Broadcaster thread failed to write frame: %s", strerror(errno));
111         break;
112       }
113       size -= written;
114       buff += written;
115     }
116   }
117 }
118 
Broadcast(int offset,const CompositionStats *)119 void VsocketScreenView::Broadcast(int offset, const CompositionStats*) {
120   std::lock_guard<std::mutex> lock(mutex_);
121   current_offset_ = offset;
122   current_seq_++;
123   cond_var_.notify_all();
124 }
125 
GetBuffer(int buffer_id)126 void* VsocketScreenView::GetBuffer(int buffer_id) {
127   return &inner_buffer_[buffer_size() * buffer_id];
128 }
129 
x_res() const130 int32_t VsocketScreenView::x_res() const { return x_res_; }
y_res() const131 int32_t VsocketScreenView::y_res() const { return y_res_; }
dpi() const132 int32_t VsocketScreenView::dpi() const { return dpi_; }
refresh_rate() const133 int32_t VsocketScreenView::refresh_rate() const { return refresh_rate_; }
134 
num_buffers() const135 int VsocketScreenView::num_buffers() const {
136   return inner_buffer_.size() / buffer_size();
137 }
138 
139 }  // namespace cvd
140