• 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 
21 #include <android-base/logging.h>
22 #include <android-base/strings.h>
23 #include <fruit/fruit.h>
24 #include <gflags/gflags.h>
25 #include <libyuv.h>
26 
27 #include "common/libs/fs/shared_fd.h"
28 #include "common/libs/utils/files.h"
29 #include "host/frontend/webrtc/audio_handler.h"
30 #include "host/frontend/webrtc/client_server.h"
31 #include "host/frontend/webrtc/connection_observer.h"
32 #include "host/frontend/webrtc/display_handler.h"
33 #include "host/frontend/webrtc/kernel_log_events_handler.h"
34 #include "host/frontend/webrtc/libdevice/camera_controller.h"
35 #include "host/frontend/webrtc/libdevice/local_recorder.h"
36 #include "host/frontend/webrtc/libdevice/streamer.h"
37 #include "host/frontend/webrtc/libdevice/video_sink.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_string(touch_fds, "",
46               "A list of fds to listen on for touch connections.");
47 DEFINE_int32(keyboard_fd, -1, "An fd to listen on for keyboard connections.");
48 DEFINE_int32(switches_fd, -1, "An fd to listen on for switch connections.");
49 DEFINE_int32(frame_server_fd, -1, "An fd to listen on for frame updates");
50 DEFINE_int32(kernel_log_events_fd, -1,
51              "An fd to listen on for kernel log events.");
52 DEFINE_int32(command_fd, -1, "An fd to listen to for control messages");
53 DEFINE_int32(confui_in_fd, -1,
54              "Confirmation UI virtio-console from host to guest");
55 DEFINE_int32(confui_out_fd, -1,
56              "Confirmation UI virtio-console from guest to host");
57 DEFINE_string(action_servers, "",
58               "A comma-separated list of server_name:fd pairs, "
59               "where each entry corresponds to one custom action server.");
60 DEFINE_bool(write_virtio_input, true,
61             "Whether to send input events in virtio format.");
62 DEFINE_int32(audio_server_fd, -1, "An fd to listen on for audio frames");
63 DEFINE_int32(camera_streamer_fd, -1, "An fd to send client camera frames");
64 DEFINE_string(client_dir, "webrtc", "Location of the client files");
65 
66 using cuttlefish::AudioHandler;
67 using cuttlefish::CfConnectionObserverFactory;
68 using cuttlefish::DisplayHandler;
69 using cuttlefish::KernelLogEventsHandler;
70 using cuttlefish::webrtc_streaming::LocalRecorder;
71 using cuttlefish::webrtc_streaming::Streamer;
72 using cuttlefish::webrtc_streaming::StreamerConfig;
73 using cuttlefish::webrtc_streaming::VideoSink;
74 using cuttlefish::webrtc_streaming::ServerConfig;
75 
76 class CfOperatorObserver
77     : public cuttlefish::webrtc_streaming::OperatorObserver {
78  public:
79   virtual ~CfOperatorObserver() = default;
OnRegistered()80   virtual void OnRegistered() override {
81     LOG(VERBOSE) << "Registered with Operator";
82   }
OnClose()83   virtual void OnClose() override {
84     LOG(ERROR) << "Connection with Operator unexpectedly closed";
85   }
OnError()86   virtual void OnError() override {
87     LOG(ERROR) << "Error encountered in connection with Operator";
88   }
89 };
CreateAudioServer()90 std::unique_ptr<cuttlefish::AudioServer> CreateAudioServer() {
91   cuttlefish::SharedFD audio_server_fd =
92       cuttlefish::SharedFD::Dup(FLAGS_audio_server_fd);
93   close(FLAGS_audio_server_fd);
94   return std::make_unique<cuttlefish::AudioServer>(audio_server_fd);
95 }
96 
WebRtcComponent()97 fruit::Component<cuttlefish::CustomActionConfigProvider> WebRtcComponent() {
98   return fruit::createComponent()
99       .install(cuttlefish::ConfigFlagPlaceholder)
100       .install(cuttlefish::CustomActionsComponent);
101 };
102 
103 fruit::Component<
104     cuttlefish::ScreenConnector<DisplayHandler::WebRtcScProcessedFrame>,
105     cuttlefish::confui::HostServer, cuttlefish::confui::HostVirtualInput>
CreateConfirmationUIComponent(int * frames_fd,cuttlefish::confui::PipeConnectionPair * pipe_io_pair)106 CreateConfirmationUIComponent(
107     int* frames_fd, cuttlefish::confui::PipeConnectionPair* pipe_io_pair) {
108   using cuttlefish::ScreenConnectorFrameRenderer;
109   using ScreenConnector = cuttlefish::DisplayHandler::ScreenConnector;
110   return fruit::createComponent()
111       .bindInstance<
112           fruit::Annotated<cuttlefish::WaylandScreenConnector::FramesFd, int>>(
113           *frames_fd)
114       .bindInstance(*pipe_io_pair)
115       .bind<ScreenConnectorFrameRenderer, ScreenConnector>();
116 }
117 
main(int argc,char ** argv)118 int main(int argc, char** argv) {
119   cuttlefish::DefaultSubprocessLogging(argv);
120   ::gflags::ParseCommandLineFlags(&argc, &argv, true);
121 
122   cuttlefish::InputSockets input_sockets;
123 
124   auto counter = 0;
125   for (const auto& touch_fd_str : android::base::Split(FLAGS_touch_fds, ",")) {
126     auto touch_fd = std::stoi(touch_fd_str);
127     input_sockets.touch_servers["display_" + std::to_string(counter++)] =
128         cuttlefish::SharedFD::Dup(touch_fd);
129     close(touch_fd);
130   }
131   input_sockets.keyboard_server = cuttlefish::SharedFD::Dup(FLAGS_keyboard_fd);
132   input_sockets.switches_server = cuttlefish::SharedFD::Dup(FLAGS_switches_fd);
133   auto control_socket = cuttlefish::SharedFD::Dup(FLAGS_command_fd);
134   close(FLAGS_keyboard_fd);
135   close(FLAGS_switches_fd);
136   close(FLAGS_command_fd);
137   // Accepting on these sockets here means the device won't register with the
138   // operator as soon as it could, but rather wait until crosvm's input display
139   // devices have been initialized. That's OK though, because without those
140   // devices there is no meaningful interaction the user can have with the
141   // device.
142   for (const auto& touch_entry : input_sockets.touch_servers) {
143     input_sockets.touch_clients[touch_entry.first] =
144         cuttlefish::SharedFD::Accept(*touch_entry.second);
145   }
146   input_sockets.keyboard_client =
147       cuttlefish::SharedFD::Accept(*input_sockets.keyboard_server);
148   input_sockets.switches_client =
149       cuttlefish::SharedFD::Accept(*input_sockets.switches_server);
150 
151   std::vector<std::thread> touch_accepters;
152   touch_accepters.reserve(input_sockets.touch_servers.size());
153   for (const auto& touch : input_sockets.touch_servers) {
154     auto label = touch.first;
155     touch_accepters.emplace_back([label, &input_sockets]() {
156       for (;;) {
157         input_sockets.touch_clients[label] =
158             cuttlefish::SharedFD::Accept(*input_sockets.touch_servers[label]);
159       }
160     });
161   }
162   std::thread keyboard_accepter([&input_sockets]() {
163     for (;;) {
164       input_sockets.keyboard_client =
165           cuttlefish::SharedFD::Accept(*input_sockets.keyboard_server);
166     }
167   });
168   std::thread switches_accepter([&input_sockets]() {
169     for (;;) {
170       input_sockets.switches_client =
171           cuttlefish::SharedFD::Accept(*input_sockets.switches_server);
172     }
173   });
174 
175   auto kernel_log_events_client =
176       cuttlefish::SharedFD::Dup(FLAGS_kernel_log_events_fd);
177   close(FLAGS_kernel_log_events_fd);
178 
179   auto cvd_config = cuttlefish::CuttlefishConfig::Get();
180   auto instance = cvd_config->ForDefaultInstance();
181 
182   cuttlefish::confui::PipeConnectionPair conf_ui_comm_fd_pair{
183       .from_guest_ = cuttlefish::SharedFD::Dup(FLAGS_confui_out_fd),
184       .to_guest_ = cuttlefish::SharedFD::Dup(FLAGS_confui_in_fd)};
185   close(FLAGS_confui_in_fd);
186   close(FLAGS_confui_out_fd);
187 
188   int frames_fd = FLAGS_frame_server_fd;
189   fruit::Injector<
190       cuttlefish::ScreenConnector<DisplayHandler::WebRtcScProcessedFrame>,
191       cuttlefish::confui::HostServer, cuttlefish::confui::HostVirtualInput>
192       conf_ui_components_injector(CreateConfirmationUIComponent,
193                                   std::addressof(frames_fd),
194                                   &conf_ui_comm_fd_pair);
195   auto& screen_connector =
196       conf_ui_components_injector.get<DisplayHandler::ScreenConnector&>();
197 
198   auto client_server = cuttlefish::ClientFilesServer::New(FLAGS_client_dir);
199   CHECK(client_server) << "Failed to initialize client files server";
200   auto& host_confui_server =
201       conf_ui_components_injector.get<cuttlefish::confui::HostServer&>();
202   auto& confui_virtual_input =
203       conf_ui_components_injector.get<cuttlefish::confui::HostVirtualInput&>();
204 
205   StreamerConfig streamer_config;
206 
207   streamer_config.device_id = instance.webrtc_device_id();
208   streamer_config.client_files_port = client_server->port();
209   streamer_config.tcp_port_range = instance.webrtc_tcp_port_range();
210   streamer_config.udp_port_range = instance.webrtc_udp_port_range();
211   streamer_config.operator_server.addr = cvd_config->sig_server_address();
212   streamer_config.operator_server.port = cvd_config->sig_server_port();
213   streamer_config.operator_server.path = cvd_config->sig_server_path();
214   if (cvd_config->sig_server_secure()) {
215     streamer_config.operator_server.security =
216         cvd_config->sig_server_strict()
217             ? ServerConfig::Security::kStrict
218             : ServerConfig::Security::kAllowSelfSigned;
219   } else {
220     streamer_config.operator_server.security =
221         ServerConfig::Security::kInsecure;
222   }
223 
224   KernelLogEventsHandler kernel_logs_event_handler(kernel_log_events_client);
225   auto observer_factory = std::make_shared<CfConnectionObserverFactory>(
226       input_sockets, &kernel_logs_event_handler, confui_virtual_input);
227 
228   // The recorder is created first, so displays added in callbacks to the
229   // Streamer can also be added to the LocalRecorder.
230   std::unique_ptr<cuttlefish::webrtc_streaming::LocalRecorder> local_recorder;
231   if (instance.record_screen()) {
232     int recording_num = 0;
233     std::string recording_path;
234     do {
235       recording_path = instance.PerInstancePath("recording/recording_");
236       recording_path += std::to_string(recording_num);
237       recording_path += ".webm";
238       recording_num++;
239     } while (cuttlefish::FileExists(recording_path));
240     local_recorder = LocalRecorder::Create(recording_path);
241     CHECK(local_recorder) << "Could not create local recorder";
242   }
243 
244   auto streamer =
245       Streamer::Create(streamer_config, local_recorder.get(), observer_factory);
246   CHECK(streamer) << "Could not create streamer";
247 
248   auto display_handler =
249       std::make_shared<DisplayHandler>(*streamer, screen_connector);
250 
251   if (instance.camera_server_port()) {
252     auto camera_controller = streamer->AddCamera(instance.camera_server_port(),
253                                                  instance.vsock_guest_cid());
254     observer_factory->SetCameraHandler(camera_controller);
255   }
256 
257   observer_factory->SetDisplayHandler(display_handler);
258 
259   streamer->SetHardwareSpec("CPUs", instance.cpus());
260   streamer->SetHardwareSpec("RAM", std::to_string(instance.memory_mb()) + " mb");
261 
262   std::string user_friendly_gpu_mode;
263   if (instance.gpu_mode() == cuttlefish::kGpuModeGuestSwiftshader) {
264     user_friendly_gpu_mode = "SwiftShader (Guest CPU Rendering)";
265   } else if (instance.gpu_mode() == cuttlefish::kGpuModeDrmVirgl) {
266     user_friendly_gpu_mode =
267         "VirglRenderer (Accelerated Rendering using Host OpenGL)";
268   } else if (instance.gpu_mode() == cuttlefish::kGpuModeGfxstream) {
269     user_friendly_gpu_mode =
270         "Gfxstream (Accelerated Rendering using Host OpenGL and Vulkan)";
271   } else if (instance.gpu_mode() == cuttlefish::kGpuModeGfxstreamGuestAngle) {
272     user_friendly_gpu_mode =
273         "Gfxstream (Accelerated Rendering using Host Vulkan)";
274   } else {
275     user_friendly_gpu_mode = instance.gpu_mode();
276   }
277   streamer->SetHardwareSpec("GPU Mode", user_friendly_gpu_mode);
278 
279   std::shared_ptr<AudioHandler> audio_handler;
280   if (instance.enable_audio()) {
281     auto audio_stream = streamer->AddAudioStream("audio");
282     auto audio_server = CreateAudioServer();
283     auto audio_source = streamer->GetAudioSource();
284     audio_handler = std::make_shared<AudioHandler>(std::move(audio_server),
285                                                    audio_stream, audio_source);
286   }
287 
288   // Parse the -action_servers flag, storing a map of action server name -> fd
289   std::map<std::string, int> action_server_fds;
290   for (const std::string& action_server :
291        android::base::Split(FLAGS_action_servers, ",")) {
292     if (action_server.empty()) {
293       continue;
294     }
295     const std::vector<std::string> server_and_fd =
296         android::base::Split(action_server, ":");
297     CHECK(server_and_fd.size() == 2)
298         << "Wrong format for action server flag: " << action_server;
299     std::string server = server_and_fd[0];
300     int fd = std::stoi(server_and_fd[1]);
301     action_server_fds[server] = fd;
302   }
303 
304   fruit::Injector<cuttlefish::CustomActionConfigProvider> injector(
305       WebRtcComponent);
306   for (auto& fragment :
307        injector.getMultibindings<cuttlefish::ConfigFragment>()) {
308     CHECK(cvd_config->LoadFragment(*fragment))
309         << "Failed to load config fragment";
310   }
311 
312   const auto& actions_provider =
313       injector.get<cuttlefish::CustomActionConfigProvider&>();
314 
315   for (const auto& custom_action :
316        actions_provider.CustomShellActions(instance.id())) {
317     const auto button = custom_action.button;
318     streamer->AddCustomControlPanelButtonWithShellCommand(
319         button.command, button.title, button.icon_name,
320         custom_action.shell_command);
321   }
322 
323   for (const auto& custom_action :
324        actions_provider.CustomActionServers(instance.id())) {
325     if (action_server_fds.find(custom_action.server) ==
326         action_server_fds.end()) {
327       LOG(ERROR) << "Custom action server not provided as command line flag: "
328                  << custom_action.server;
329       continue;
330     }
331     LOG(INFO) << "Connecting to custom action server " << custom_action.server;
332 
333     int fd = action_server_fds[custom_action.server];
334     cuttlefish::SharedFD custom_action_server = cuttlefish::SharedFD::Dup(fd);
335     close(fd);
336 
337     if (custom_action_server->IsOpen()) {
338       std::vector<std::string> commands_for_this_server;
339       for (const auto& button : custom_action.buttons) {
340         streamer->AddCustomControlPanelButton(button.command, button.title,
341                                               button.icon_name);
342         commands_for_this_server.push_back(button.command);
343       }
344       observer_factory->AddCustomActionServer(custom_action_server,
345                                               commands_for_this_server);
346     } else {
347       LOG(ERROR) << "Error connecting to custom action server: "
348                  << custom_action.server;
349     }
350   }
351 
352   for (const auto& custom_action :
353        actions_provider.CustomDeviceStateActions(instance.id())) {
354     const auto button = custom_action.button;
355     streamer->AddCustomControlPanelButtonWithDeviceStates(
356         button.command, button.title, button.icon_name,
357         custom_action.device_states);
358   }
359 
360   std::shared_ptr<cuttlefish::webrtc_streaming::OperatorObserver> operator_observer(
361       new CfOperatorObserver());
362   streamer->Register(operator_observer);
363 
364   std::thread control_thread([control_socket, &local_recorder]() {
365     if (!local_recorder) {
366       return;
367     }
368     std::string message = "_";
369     int read_ret;
370     while ((read_ret = cuttlefish::ReadExact(control_socket, &message)) > 0) {
371       LOG(VERBOSE) << "received control message: " << message;
372       if (message[0] == 'C') {
373         LOG(DEBUG) << "Finalizing screen recording...";
374         local_recorder->Stop();
375         LOG(INFO) << "Finalized screen recording.";
376         message = "Y";
377         cuttlefish::WriteAll(control_socket, message);
378       }
379     }
380     LOG(DEBUG) << "control socket closed";
381   });
382 
383   if (audio_handler) {
384     audio_handler->Start();
385   }
386   host_confui_server.Start();
387   display_handler->Loop();
388 
389   return 0;
390 }
391