• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 #include <linux/input.h>
18 
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include <android-base/logging.h>
25 #include <android-base/strings.h>
26 #include <gflags/gflags.h>
27 #include <libyuv.h>
28 
29 #include "common/libs/fs/shared_buf.h"
30 #include "common/libs/fs/shared_fd.h"
31 #include "common/libs/utils/files.h"
32 #include "host/frontend/webrtc/audio_handler.h"
33 #include "host/frontend/webrtc/connection_observer.h"
34 #include "host/frontend/webrtc/display_handler.h"
35 #include "host/frontend/webrtc/kernel_log_events_handler.h"
36 #include "host/frontend/webrtc/lib/local_recorder.h"
37 #include "host/frontend/webrtc/lib/streamer.h"
38 #include "host/libs/audio_connector/server.h"
39 #include "host/libs/config/cuttlefish_config.h"
40 #include "host/libs/config/logging.h"
41 #include "host/libs/confui/host_mode_ctrl.h"
42 #include "host/libs/confui/host_server.h"
43 #include "host/libs/screen_connector/screen_connector.h"
44 
45 DEFINE_int32(touch_fd, -1, "An fd to listen on for touch connections.");
46 DEFINE_int32(keyboard_fd, -1, "An fd to listen on for keyboard connections.");
47 DEFINE_int32(switches_fd, -1, "An fd to listen on for switch connections.");
48 DEFINE_int32(frame_server_fd, -1, "An fd to listen on for frame updates");
49 DEFINE_int32(kernel_log_events_fd, -1,
50              "An fd to listen on for kernel log events.");
51 DEFINE_int32(command_fd, -1, "An fd to listen to for control messages");
52 DEFINE_string(action_servers, "",
53               "A comma-separated list of server_name:fd pairs, "
54               "where each entry corresponds to one custom action server.");
55 DEFINE_bool(write_virtio_input, true,
56             "Whether to send input events in virtio format.");
57 DEFINE_int32(audio_server_fd, -1, "An fd to listen on for audio frames");
58 
59 using cuttlefish::AudioHandler;
60 using cuttlefish::CfConnectionObserverFactory;
61 using cuttlefish::DisplayHandler;
62 using cuttlefish::KernelLogEventsHandler;
63 using cuttlefish::webrtc_streaming::LocalRecorder;
64 using cuttlefish::webrtc_streaming::Streamer;
65 using cuttlefish::webrtc_streaming::StreamerConfig;
66 
67 class CfOperatorObserver
68     : public cuttlefish::webrtc_streaming::OperatorObserver {
69  public:
70   virtual ~CfOperatorObserver() = default;
OnRegistered()71   virtual void OnRegistered() override {
72     LOG(VERBOSE) << "Registered with Operator";
73   }
OnClose()74   virtual void OnClose() override {
75     LOG(ERROR) << "Connection with Operator unexpectedly closed";
76   }
OnError()77   virtual void OnError() override {
78     LOG(ERROR) << "Error encountered in connection with Operator";
79   }
80 };
81 
ParseHttpHeaders(const std::string & path)82 static std::vector<std::pair<std::string, std::string>> ParseHttpHeaders(
83     const std::string& path) {
84   auto fd = cuttlefish::SharedFD::Open(path, O_RDONLY);
85   if (!fd->IsOpen()) {
86     LOG(WARNING) << "Unable to open operator (signaling server) headers file, "
87                     "connecting to the operator will probably fail: "
88                  << fd->StrError();
89     return {};
90   }
91   std::string raw_headers;
92   auto res = cuttlefish::ReadAll(fd, &raw_headers);
93   if (res < 0) {
94     LOG(WARNING) << "Unable to open operator (signaling server) headers file, "
95                     "connecting to the operator will probably fail: "
96                  << fd->StrError();
97     return {};
98   }
99   std::vector<std::pair<std::string, std::string>> headers;
100   std::size_t raw_index = 0;
101   while (raw_index < raw_headers.size()) {
102     auto colon_pos = raw_headers.find(':', raw_index);
103     if (colon_pos == std::string::npos) {
104       LOG(ERROR)
105           << "Expected to find ':' in each line of the operator headers file";
106       break;
107     }
108     auto eol_pos = raw_headers.find('\n', colon_pos);
109     if (eol_pos == std::string::npos) {
110       eol_pos = raw_headers.size();
111     }
112     // If the file uses \r\n as line delimiters exclude the \r too.
113     auto eov_pos = raw_headers[eol_pos - 1] == '\r'? eol_pos - 1: eol_pos;
114     headers.emplace_back(
115         raw_headers.substr(raw_index, colon_pos + 1 - raw_index),
116         raw_headers.substr(colon_pos + 1, eov_pos - colon_pos - 1));
117     raw_index = eol_pos + 1;
118   }
119   return headers;
120 }
121 
CreateAudioServer()122 std::unique_ptr<cuttlefish::AudioServer> CreateAudioServer() {
123   cuttlefish::SharedFD audio_server_fd =
124       cuttlefish::SharedFD::Dup(FLAGS_audio_server_fd);
125   close(FLAGS_audio_server_fd);
126   return std::make_unique<cuttlefish::AudioServer>(audio_server_fd);
127 }
128 
main(int argc,char ** argv)129 int main(int argc, char** argv) {
130   cuttlefish::DefaultSubprocessLogging(argv);
131   ::gflags::ParseCommandLineFlags(&argc, &argv, true);
132 
133   cuttlefish::InputSockets input_sockets;
134 
135   input_sockets.touch_server = cuttlefish::SharedFD::Dup(FLAGS_touch_fd);
136   input_sockets.keyboard_server = cuttlefish::SharedFD::Dup(FLAGS_keyboard_fd);
137   input_sockets.switches_server = cuttlefish::SharedFD::Dup(FLAGS_switches_fd);
138   auto control_socket = cuttlefish::SharedFD::Dup(FLAGS_command_fd);
139   close(FLAGS_touch_fd);
140   close(FLAGS_keyboard_fd);
141   close(FLAGS_switches_fd);
142   close(FLAGS_command_fd);
143   // Accepting on these sockets here means the device won't register with the
144   // operator as soon as it could, but rather wait until crosvm's input display
145   // devices have been initialized. That's OK though, because without those
146   // devices there is no meaningful interaction the user can have with the
147   // device.
148   input_sockets.touch_client =
149       cuttlefish::SharedFD::Accept(*input_sockets.touch_server);
150   input_sockets.keyboard_client =
151       cuttlefish::SharedFD::Accept(*input_sockets.keyboard_server);
152   input_sockets.switches_client =
153       cuttlefish::SharedFD::Accept(*input_sockets.switches_server);
154 
155   std::thread touch_accepter([&input_sockets]() {
156     for (;;) {
157       input_sockets.touch_client =
158           cuttlefish::SharedFD::Accept(*input_sockets.touch_server);
159     }
160   });
161   std::thread keyboard_accepter([&input_sockets]() {
162     for (;;) {
163       input_sockets.keyboard_client =
164           cuttlefish::SharedFD::Accept(*input_sockets.keyboard_server);
165     }
166   });
167   std::thread switches_accepter([&input_sockets]() {
168     for (;;) {
169       input_sockets.switches_client =
170           cuttlefish::SharedFD::Accept(*input_sockets.switches_server);
171     }
172   });
173 
174   auto kernel_log_events_client =
175       cuttlefish::SharedFD::Dup(FLAGS_kernel_log_events_fd);
176   close(FLAGS_kernel_log_events_fd);
177 
178   auto cvd_config = cuttlefish::CuttlefishConfig::Get();
179   auto instance = cvd_config->ForDefaultInstance();
180   auto& host_mode_ctrl = cuttlefish::HostModeCtrl::Get();
181   auto screen_connector_ptr = cuttlefish::DisplayHandler::ScreenConnector::Get(
182       FLAGS_frame_server_fd, host_mode_ctrl);
183   auto& screen_connector = *(screen_connector_ptr.get());
184 
185   // create confirmation UI service, giving host_mode_ctrl and
186   // screen_connector
187   // keep this singleton object alive until the webRTC process ends
188   static auto& host_confui_server =
189       cuttlefish::confui::HostServer::Get(host_mode_ctrl, screen_connector);
190 
191   StreamerConfig streamer_config;
192 
193   streamer_config.device_id = instance.webrtc_device_id();
194   streamer_config.tcp_port_range = cvd_config->webrtc_tcp_port_range();
195   streamer_config.udp_port_range = cvd_config->webrtc_udp_port_range();
196   streamer_config.operator_server.addr = cvd_config->sig_server_address();
197   streamer_config.operator_server.port = cvd_config->sig_server_port();
198   streamer_config.operator_server.path = cvd_config->sig_server_path();
199   streamer_config.operator_server.security =
200       cvd_config->sig_server_strict()
201           ? WsConnection::Security::kStrict
202           : WsConnection::Security::kAllowSelfSigned;
203 
204   if (!cvd_config->sig_server_headers_path().empty()) {
205     streamer_config.operator_server.http_headers =
206         ParseHttpHeaders(cvd_config->sig_server_headers_path());
207   }
208 
209   KernelLogEventsHandler kernel_logs_event_handler(kernel_log_events_client);
210   auto observer_factory = std::make_shared<CfConnectionObserverFactory>(
211       input_sockets, &kernel_logs_event_handler, host_confui_server);
212 
213   auto streamer = Streamer::Create(streamer_config, observer_factory);
214   CHECK(streamer) << "Could not create streamer";
215 
216   auto display_0 = streamer->AddDisplay(
217       "display_0", screen_connector.ScreenWidth(0),
218       screen_connector.ScreenHeight(0), cvd_config->dpi(), true);
219   auto display_handler = std::shared_ptr<DisplayHandler>(
220       new DisplayHandler(display_0, screen_connector));
221 
222   std::unique_ptr<cuttlefish::webrtc_streaming::LocalRecorder> local_recorder;
223   if (cvd_config->record_screen()) {
224     int recording_num = 0;
225     std::string recording_path;
226     do {
227       recording_path = instance.PerInstancePath("recording/recording_");
228       recording_path += std::to_string(recording_num);
229       recording_path += ".webm";
230       recording_num++;
231     } while (cuttlefish::FileExists(recording_path));
232     local_recorder = LocalRecorder::Create(recording_path);
233     CHECK(local_recorder) << "Could not create local recorder";
234 
235     streamer->RecordDisplays(*local_recorder);
236     display_handler->IncClientCount();
237   }
238 
239   observer_factory->SetDisplayHandler(display_handler);
240 
241   streamer->SetHardwareSpec("CPUs", cvd_config->cpus());
242   streamer->SetHardwareSpec("RAM", std::to_string(cvd_config->memory_mb()) + " mb");
243 
244   std::string user_friendly_gpu_mode;
245   if (cvd_config->gpu_mode() == cuttlefish::kGpuModeGuestSwiftshader) {
246     user_friendly_gpu_mode = "SwiftShader (Guest CPU Rendering)";
247   } else if (cvd_config->gpu_mode() == cuttlefish::kGpuModeDrmVirgl) {
248     user_friendly_gpu_mode = "VirglRenderer (Accelerated Host GPU Rendering)";
249   } else if (cvd_config->gpu_mode() == cuttlefish::kGpuModeGfxStream) {
250     user_friendly_gpu_mode = "Gfxstream (Accelerated Host GPU Rendering)";
251   } else {
252     user_friendly_gpu_mode = cvd_config->gpu_mode();
253   }
254   streamer->SetHardwareSpec("GPU Mode", user_friendly_gpu_mode);
255 
256   std::shared_ptr<AudioHandler> audio_handler;
257   if (cvd_config->enable_audio()) {
258     auto audio_stream = streamer->AddAudioStream("audio");
259     auto audio_server = CreateAudioServer();
260     auto audio_source = streamer->GetAudioSource();
261     audio_handler = std::make_shared<AudioHandler>(std::move(audio_server),
262                                                    audio_stream, audio_source);
263   }
264 
265   // Parse the -action_servers flag, storing a map of action server name -> fd
266   std::map<std::string, int> action_server_fds;
267   for (const std::string& action_server :
268        android::base::Split(FLAGS_action_servers, ",")) {
269     if (action_server.empty()) {
270       continue;
271     }
272     const std::vector<std::string> server_and_fd =
273         android::base::Split(action_server, ":");
274     CHECK(server_and_fd.size() == 2)
275         << "Wrong format for action server flag: " << action_server;
276     std::string server = server_and_fd[0];
277     int fd = std::stoi(server_and_fd[1]);
278     action_server_fds[server] = fd;
279   }
280 
281   for (const auto& custom_action : cvd_config->custom_actions()) {
282     if (custom_action.shell_command) {
283       if (custom_action.buttons.size() != 1) {
284         LOG(FATAL) << "Expected exactly one button for custom action command: "
285                    << *(custom_action.shell_command);
286       }
287       const auto button = custom_action.buttons[0];
288       streamer->AddCustomControlPanelButtonWithShellCommand(
289           button.command, button.title, button.icon_name,
290           *(custom_action.shell_command));
291     } else if (custom_action.server) {
292       if (action_server_fds.find(*(custom_action.server)) !=
293           action_server_fds.end()) {
294         LOG(INFO) << "Connecting to custom action server "
295                   << *(custom_action.server);
296 
297         int fd = action_server_fds[*(custom_action.server)];
298         cuttlefish::SharedFD custom_action_server = cuttlefish::SharedFD::Dup(fd);
299         close(fd);
300 
301         if (custom_action_server->IsOpen()) {
302           std::vector<std::string> commands_for_this_server;
303           for (const auto& button : custom_action.buttons) {
304             streamer->AddCustomControlPanelButton(button.command, button.title,
305                                                   button.icon_name);
306             commands_for_this_server.push_back(button.command);
307           }
308           observer_factory->AddCustomActionServer(custom_action_server,
309                                                   commands_for_this_server);
310         } else {
311           LOG(ERROR) << "Error connecting to custom action server: "
312                      << *(custom_action.server);
313         }
314       } else {
315         LOG(ERROR) << "Custom action server not provided as command line flag: "
316                    << *(custom_action.server);
317       }
318     } else if (!custom_action.device_states.empty()) {
319       if (custom_action.buttons.size() != 1) {
320         LOG(FATAL)
321             << "Expected exactly one button for custom action device states.";
322       }
323       const auto button = custom_action.buttons[0];
324       streamer->AddCustomControlPanelButtonWithDeviceStates(
325           button.command, button.title, button.icon_name,
326           custom_action.device_states);
327     }
328   }
329 
330   std::shared_ptr<cuttlefish::webrtc_streaming::OperatorObserver> operator_observer(
331       new CfOperatorObserver());
332   streamer->Register(operator_observer);
333 
334   std::thread control_thread([control_socket, &local_recorder]() {
335     if (!local_recorder) {
336       return;
337     }
338     std::string message = "_";
339     int read_ret;
340     while ((read_ret = cuttlefish::ReadExact(control_socket, &message)) > 0) {
341       LOG(VERBOSE) << "received control message: " << message;
342       if (message[0] == 'C') {
343         LOG(DEBUG) << "Finalizing screen recording...";
344         local_recorder->Stop();
345         LOG(INFO) << "Finalized screen recording.";
346         message = "Y";
347         cuttlefish::WriteAll(control_socket, message);
348       }
349     }
350     LOG(DEBUG) << "control socket closed";
351   });
352 
353   if (audio_handler) {
354     audio_handler->Start();
355   }
356   host_confui_server.Start();
357   display_handler->Loop();
358 
359   return 0;
360 }
361