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 <memory>
18
19 #include <android-base/logging.h>
20 #include <android-base/parseint.h>
21 #include <android-base/strings.h>
22 #include <fruit/fruit.h>
23 #include <gflags/gflags.h>
24 #include <libyuv.h>
25
26 #include "common/libs/fs/shared_fd.h"
27 #include "common/libs/utils/files.h"
28 #include "google/rpc/code.pb.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/lights_observer.h"
36 #include "host/frontend/webrtc/libdevice/local_recorder.h"
37 #include "host/frontend/webrtc/libdevice/streamer.h"
38 #include "host/frontend/webrtc/libdevice/video_sink.h"
39 #include "host/frontend/webrtc/screenshot_handler.h"
40 #include "host/frontend/webrtc/webrtc_command_channel.h"
41 #include "host/libs/audio_connector/server.h"
42 #include "host/libs/config/cuttlefish_config.h"
43 #include "host/libs/config/logging.h"
44 #include "host/libs/config/openwrt_args.h"
45 #include "host/libs/confui/host_mode_ctrl.h"
46 #include "host/libs/confui/host_server.h"
47 #include "host/libs/input_connector/input_connector.h"
48 #include "host/libs/screen_connector/screen_connector.h"
49 #include "webrtc_commands.pb.h"
50
51 DEFINE_bool(multitouch, true,
52 "Whether to send multi-touch or single-touch events");
53 DEFINE_string(touch_fds, "",
54 "A list of fds to listen on for touch connections.");
55 DEFINE_int32(mouse_fd, -1, "An fd to listen on for mouse connections.");
56 DEFINE_int32(rotary_fd, -1, "An fd to listen on for rotary connections.");
57 DEFINE_int32(keyboard_fd, -1, "An fd to listen on for keyboard connections.");
58 DEFINE_int32(switches_fd, -1, "An fd to listen on for switch connections.");
59 DEFINE_int32(frame_server_fd, -1, "An fd to listen on for frame updates");
60 DEFINE_int32(kernel_log_events_fd, -1,
61 "An fd to listen on for kernel log events.");
62 DEFINE_int32(command_fd, -1, "An fd to listen to for control messages");
63 DEFINE_int32(confui_in_fd, -1,
64 "Confirmation UI virtio-console from host to guest");
65 DEFINE_int32(confui_out_fd, -1,
66 "Confirmation UI virtio-console from guest to host");
67 DEFINE_int32(sensors_in_fd, -1, "Sensors virtio-console from host to guest");
68 DEFINE_int32(sensors_out_fd, -1, "Sensors virtio-console from guest to host");
69 DEFINE_string(action_servers, "",
70 "A comma-separated list of server_name:fd pairs, "
71 "where each entry corresponds to one custom action server.");
72 DEFINE_bool(write_virtio_input, true,
73 "Whether to send input events in virtio format.");
74 DEFINE_int32(audio_server_fd, -1, "An fd to listen on for audio frames");
75 DEFINE_int32(camera_streamer_fd, -1, "An fd to send client camera frames");
76 DEFINE_string(client_dir, "webrtc", "Location of the client files");
77 DEFINE_string(group_id, "", "The group id of device");
78
79 namespace cuttlefish {
80
81 using webrtc_streaming::RecordingManager;
82 using webrtc_streaming::ServerConfig;
83 using webrtc_streaming::Streamer;
84 using webrtc_streaming::StreamerConfig;
85 using webrtc_streaming::VideoSink;
86
87 constexpr auto kOpewnrtWanIpAddressName = "wan_ipaddr";
88 constexpr auto kTouchscreenPrefix = "display_";
89 constexpr auto kTouchpadPrefix = "touch_";
90
91 class CfOperatorObserver : public webrtc_streaming::OperatorObserver {
92 public:
93 virtual ~CfOperatorObserver() = default;
OnRegistered()94 virtual void OnRegistered() override {
95 LOG(VERBOSE) << "Registered with Operator";
96 }
OnClose()97 virtual void OnClose() override {
98 LOG(ERROR) << "Connection with Operator unexpectedly closed";
99 }
OnError()100 virtual void OnError() override {
101 LOG(ERROR) << "Error encountered in connection with Operator";
102 }
103 };
CreateAudioServer()104 std::unique_ptr<AudioServer> CreateAudioServer() {
105 SharedFD audio_server_fd = SharedFD::Dup(FLAGS_audio_server_fd);
106 close(FLAGS_audio_server_fd);
107 return std::make_unique<AudioServer>(audio_server_fd);
108 }
109
WebRtcComponent()110 fruit::Component<CustomActionConfigProvider> WebRtcComponent() {
111 return fruit::createComponent()
112 .install(ConfigFlagPlaceholder)
113 .install(CustomActionsComponent);
114 };
115
116 fruit::Component<ScreenConnector<DisplayHandler::WebRtcScProcessedFrame>,
117 confui::HostServer, confui::HostVirtualInput>
CreateConfirmationUIComponent(int * frames_fd,bool * frames_are_rgba,confui::PipeConnectionPair * pipe_io_pair,InputConnector * input_connector)118 CreateConfirmationUIComponent(int* frames_fd, bool* frames_are_rgba,
119 confui::PipeConnectionPair* pipe_io_pair,
120 InputConnector* input_connector) {
121 using ScreenConnector = DisplayHandler::ScreenConnector;
122 return fruit::createComponent()
123 .bindInstance<fruit::Annotated<WaylandScreenConnector::FramesFd, int>>(
124 *frames_fd)
125 .bindInstance<
126 fruit::Annotated<WaylandScreenConnector::FramesAreRgba, bool>>(
127 *frames_are_rgba)
128 .bindInstance(*pipe_io_pair)
129 .bind<ScreenConnectorFrameRenderer, ScreenConnector>()
130 .bindInstance(*input_connector);
131 }
132
ControlLoop(SharedFD control_socket,DisplayHandler & display_handler,RecordingManager & recording_manager,ScreenshotHandler & screenshot_handler)133 Result<void> ControlLoop(SharedFD control_socket,
134 DisplayHandler& display_handler,
135 RecordingManager& recording_manager,
136 ScreenshotHandler& screenshot_handler) {
137 WebrtcServerCommandChannel channel(control_socket);
138 while (true) {
139 webrtc::WebrtcCommandRequest request = CF_EXPECT(channel.ReceiveRequest());
140
141 Result<void> command_result = {};
142 if (request.has_start_recording_request()) {
143 LOG(INFO) << "Received command to start recording in main.cpp.";
144 recording_manager.Start();
145 } else if (request.has_stop_recording_request()) {
146 LOG(INFO) << "Received command to stop recording in main.cpp.";
147 recording_manager.Stop();
148 } else if (request.has_screenshot_display_request()) {
149 const auto& screenshot_request = request.screenshot_display_request();
150 LOG(INFO) << "Received command to screenshot display "
151 << screenshot_request.display_number() << "in main.cpp.";
152
153 display_handler.AddDisplayClient();
154
155 command_result =
156 screenshot_handler.Screenshot(screenshot_request.display_number(),
157 screenshot_request.screenshot_path());
158
159 display_handler.RemoveDisplayClient();
160
161 if (!command_result.ok()) {
162 LOG(ERROR) << "Failed to screenshot display "
163 << screenshot_request.display_number() << " to "
164 << screenshot_request.screenshot_path() << ":"
165 << command_result.error().Message();
166 }
167 } else {
168 LOG(FATAL) << "Unhandled request: " << request.DebugString();
169 }
170
171 webrtc::WebrtcCommandResponse response;
172 auto* response_status = response.mutable_status();
173 if (command_result.ok()) {
174 response_status->set_code(google::rpc::Code::OK);
175 } else {
176 response_status->set_code(google::rpc::Code::INTERNAL);
177 response_status->set_message(command_result.error().Message());
178 }
179
180 CF_EXPECT(channel.SendResponse(response));
181 }
182 }
183
CuttlefishMain()184 int CuttlefishMain() {
185 auto control_socket = SharedFD::Dup(FLAGS_command_fd);
186 close(FLAGS_command_fd);
187
188 auto cvd_config = CuttlefishConfig::Get();
189 auto instance = cvd_config->ForDefaultInstance();
190
191 cuttlefish::InputConnectorBuilder inputs_builder(
192 FLAGS_write_virtio_input ? cuttlefish::InputEventType::Virtio
193 : cuttlefish::InputEventType::Evdev);
194
195 const auto display_count = instance.display_configs().size();
196 const auto touch_fds = android::base::Split(FLAGS_touch_fds, ",");
197 CHECK(touch_fds.size() == display_count + instance.touchpad_configs().size())
198 << "Number of touch FDs does not match the number of configured displays "
199 "and touchpads";
200 for (int i = 0; i < touch_fds.size(); i++) {
201 int touch_fd;
202 CHECK(android::base::ParseInt(touch_fds[i], &touch_fd))
203 << "Invalid touch_fd: " << touch_fds[i];
204 // Displays are listed first, then touchpads
205 auto label_prefix =
206 i < display_count ? kTouchscreenPrefix : kTouchpadPrefix;
207 auto device_idx = i < display_count ? i : i - display_count;
208 auto device_label = fmt::format("{}{}", label_prefix, device_idx);
209 auto touch_shared_fd = SharedFD::Dup(touch_fd);
210 if (FLAGS_multitouch) {
211 inputs_builder.WithMultitouchDevice(device_label, touch_shared_fd);
212 } else {
213 inputs_builder.WithTouchDevice(device_label, touch_shared_fd);
214 }
215 close(touch_fd);
216 }
217 if (FLAGS_rotary_fd >= 0) {
218 inputs_builder.WithRotary(SharedFD::Dup(FLAGS_rotary_fd));
219 close(FLAGS_rotary_fd);
220 }
221 if (FLAGS_mouse_fd >= 0) {
222 inputs_builder.WithMouse(SharedFD::Dup(FLAGS_mouse_fd));
223 close(FLAGS_mouse_fd);
224 }
225 if (FLAGS_keyboard_fd >= 0) {
226 inputs_builder.WithKeyboard(SharedFD::Dup(FLAGS_keyboard_fd));
227 close(FLAGS_keyboard_fd);
228 }
229 if (FLAGS_switches_fd >= 0) {
230 inputs_builder.WithSwitches(SharedFD::Dup(FLAGS_switches_fd));
231 close(FLAGS_switches_fd);
232 }
233
234 auto input_connector = std::move(inputs_builder).Build();
235
236 auto kernel_log_events_client = SharedFD::Dup(FLAGS_kernel_log_events_fd);
237 close(FLAGS_kernel_log_events_fd);
238
239 confui::PipeConnectionPair conf_ui_comm_fd_pair{
240 .from_guest_ = SharedFD::Dup(FLAGS_confui_out_fd),
241 .to_guest_ = SharedFD::Dup(FLAGS_confui_in_fd)};
242 close(FLAGS_confui_in_fd);
243 close(FLAGS_confui_out_fd);
244
245 int frames_fd = FLAGS_frame_server_fd;
246 bool frames_are_rgba = true;
247 fruit::Injector<ScreenConnector<DisplayHandler::WebRtcScProcessedFrame>,
248 confui::HostServer, confui::HostVirtualInput>
249 conf_ui_components_injector(CreateConfirmationUIComponent,
250 std::addressof(frames_fd),
251 std::addressof(frames_are_rgba),
252 &conf_ui_comm_fd_pair, input_connector.get());
253 auto& screen_connector =
254 conf_ui_components_injector.get<DisplayHandler::ScreenConnector&>();
255
256 auto client_server = ClientFilesServer::New(FLAGS_client_dir);
257 CHECK(client_server) << "Failed to initialize client files server";
258 auto& host_confui_server =
259 conf_ui_components_injector.get<confui::HostServer&>();
260 auto& confui_virtual_input =
261 conf_ui_components_injector.get<confui::HostVirtualInput&>();
262
263 StreamerConfig streamer_config;
264
265 streamer_config.device_id = instance.webrtc_device_id();
266 streamer_config.group_id = FLAGS_group_id;
267 streamer_config.client_files_port = client_server->port();
268 streamer_config.tcp_port_range = instance.webrtc_tcp_port_range();
269 streamer_config.udp_port_range = instance.webrtc_udp_port_range();
270 streamer_config.openwrt_device_id =
271 cvd_config->Instances()[0].webrtc_device_id();
272 streamer_config.openwrt_addr = OpenwrtArgsFromConfig(
273 cvd_config->Instances()[0])[kOpewnrtWanIpAddressName];
274 streamer_config.adb_port = instance.adb_host_port();
275 streamer_config.control_env_proxy_server_path =
276 instance.grpc_socket_path() + "/ControlEnvProxyServer.sock";
277 streamer_config.operator_server.addr = cvd_config->sig_server_address();
278 streamer_config.operator_server.port = cvd_config->sig_server_port();
279 streamer_config.operator_server.path = cvd_config->sig_server_path();
280 if (cvd_config->sig_server_secure()) {
281 streamer_config.operator_server.security =
282 cvd_config->sig_server_strict()
283 ? ServerConfig::Security::kStrict
284 : ServerConfig::Security::kAllowSelfSigned;
285 } else {
286 streamer_config.operator_server.security =
287 ServerConfig::Security::kInsecure;
288 }
289 streamer_config.enable_mouse = instance.enable_mouse();
290
291 KernelLogEventsHandler kernel_logs_event_handler(kernel_log_events_client);
292
293 std::shared_ptr<webrtc_streaming::LightsObserver> lights_observer;
294 if (instance.lights_server_port()) {
295 lights_observer = std::make_shared<webrtc_streaming::LightsObserver>(
296 instance.lights_server_port(), instance.vsock_guest_cid(),
297 instance.vhost_user_vsock());
298 lights_observer->Start();
299 }
300
301 auto observer_factory = std::make_shared<CfConnectionObserverFactory>(
302 confui_virtual_input, &kernel_logs_event_handler, lights_observer);
303
304 RecordingManager recording_manager;
305
306 ScreenshotHandler screenshot_handler;
307
308 auto streamer =
309 Streamer::Create(streamer_config, recording_manager, observer_factory);
310 CHECK(streamer) << "Could not create streamer";
311
312 auto display_handler = std::make_shared<DisplayHandler>(
313 *streamer, screenshot_handler, screen_connector);
314
315 if (instance.camera_server_port()) {
316 auto camera_controller = streamer->AddCamera(instance.camera_server_port(),
317 instance.vsock_guest_cid(),
318 instance.vhost_user_vsock());
319 observer_factory->SetCameraHandler(camera_controller);
320 streamer->SetHardwareSpec("camera_passthrough", true);
321 }
322
323 observer_factory->SetDisplayHandler(display_handler);
324
325 const auto touchpad_configs = instance.touchpad_configs();
326 for (int i = 0; i < touchpad_configs.size(); i++) {
327 streamer->AddTouchpad(kTouchpadPrefix + std::to_string(i),
328 touchpad_configs[i].width,
329 touchpad_configs[i].height);
330 }
331
332 streamer->SetHardwareSpec("CPUs", instance.cpus());
333 streamer->SetHardwareSpec("RAM", std::to_string(instance.memory_mb()) + " mb");
334
335 std::string user_friendly_gpu_mode;
336 if (instance.gpu_mode() == kGpuModeGuestSwiftshader) {
337 user_friendly_gpu_mode = "SwiftShader (Guest CPU Rendering)";
338 } else if (instance.gpu_mode() == kGpuModeDrmVirgl) {
339 user_friendly_gpu_mode =
340 "VirglRenderer (Accelerated Rendering using Host OpenGL)";
341 } else if (instance.gpu_mode() == kGpuModeGfxstream) {
342 user_friendly_gpu_mode =
343 "Gfxstream (Accelerated Rendering using Host OpenGL and Vulkan)";
344 } else if (instance.gpu_mode() == kGpuModeGfxstreamGuestAngle) {
345 user_friendly_gpu_mode =
346 "Gfxstream (Accelerated Rendering using Host Vulkan)";
347 } else {
348 user_friendly_gpu_mode = instance.gpu_mode();
349 }
350 streamer->SetHardwareSpec("GPU Mode", user_friendly_gpu_mode);
351
352 std::shared_ptr<AudioHandler> audio_handler;
353 if (instance.enable_audio()) {
354 auto audio_stream = streamer->AddAudioStream("audio");
355 auto audio_server = CreateAudioServer();
356 auto audio_source = streamer->GetAudioSource();
357 audio_handler = std::make_shared<AudioHandler>(std::move(audio_server),
358 audio_stream, audio_source);
359 }
360
361 // Parse the -action_servers flag, storing a map of action server name -> fd
362 std::map<std::string, int> action_server_fds;
363 for (const std::string& action_server :
364 android::base::Split(FLAGS_action_servers, ",")) {
365 if (action_server.empty()) {
366 continue;
367 }
368 const std::vector<std::string> server_and_fd =
369 android::base::Split(action_server, ":");
370 CHECK(server_and_fd.size() == 2)
371 << "Wrong format for action server flag: " << action_server;
372 std::string server = server_and_fd[0];
373 int fd = std::stoi(server_and_fd[1]);
374 action_server_fds[server] = fd;
375 }
376
377 fruit::Injector<CustomActionConfigProvider> injector(WebRtcComponent);
378 for (auto& fragment : injector.getMultibindings<ConfigFragment>()) {
379 CHECK(cvd_config->LoadFragment(*fragment))
380 << "Failed to load config fragment";
381 }
382
383 const auto& actions_provider = injector.get<CustomActionConfigProvider&>();
384
385 for (const auto& custom_action :
386 actions_provider.CustomShellActions(instance.id())) {
387 const auto button = custom_action.button;
388 streamer->AddCustomControlPanelButtonWithShellCommand(
389 button.command, button.title, button.icon_name,
390 custom_action.shell_command);
391 }
392
393 for (const auto& custom_action :
394 actions_provider.CustomActionServers(instance.id())) {
395 if (action_server_fds.find(custom_action.server) ==
396 action_server_fds.end()) {
397 LOG(ERROR) << "Custom action server not provided as command line flag: "
398 << custom_action.server;
399 continue;
400 }
401 LOG(INFO) << "Connecting to custom action server " << custom_action.server;
402
403 int fd = action_server_fds[custom_action.server];
404 SharedFD custom_action_server = SharedFD::Dup(fd);
405 close(fd);
406
407 if (custom_action_server->IsOpen()) {
408 std::vector<std::string> commands_for_this_server;
409 for (const auto& button : custom_action.buttons) {
410 streamer->AddCustomControlPanelButton(button.command, button.title,
411 button.icon_name);
412 commands_for_this_server.push_back(button.command);
413 }
414 observer_factory->AddCustomActionServer(custom_action_server,
415 commands_for_this_server);
416 } else {
417 LOG(ERROR) << "Error connecting to custom action server: "
418 << custom_action.server;
419 }
420 }
421
422 for (const auto& custom_action :
423 actions_provider.CustomDeviceStateActions(instance.id())) {
424 const auto button = custom_action.button;
425 streamer->AddCustomControlPanelButtonWithDeviceStates(
426 button.command, button.title, button.icon_name,
427 custom_action.device_states);
428 }
429
430 std::shared_ptr<webrtc_streaming::OperatorObserver> operator_observer(
431 new CfOperatorObserver());
432 streamer->Register(operator_observer);
433
434 std::thread control_thread([&]() {
435 auto result = ControlLoop(control_socket, *display_handler,
436 recording_manager, screenshot_handler);
437 if (!result.ok()) {
438 LOG(ERROR) << "Webrtc control loop error: " << result.error().Message();
439 }
440 LOG(DEBUG) << "Webrtc control thread exiting.";
441 });
442
443 if (audio_handler) {
444 audio_handler->Start();
445 }
446 host_confui_server.Start();
447
448 if (instance.record_screen()) {
449 LOG(VERBOSE) << "Waiting for recording manager initializing.";
450 recording_manager.WaitForSources(instance.display_configs().size());
451 recording_manager.Start();
452 }
453
454 display_handler->Loop();
455
456 return 0;
457 }
458
459 } // namespace cuttlefish
460
main(int argc,char ** argv)461 int main(int argc, char** argv) {
462 cuttlefish::DefaultSubprocessLogging(argv);
463 ::gflags::ParseCommandLineFlags(&argc, &argv, true);
464 return cuttlefish::CuttlefishMain();
465 }