1 //
2 // Copyright (C) 2019 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 #include "host/commands/run_cvd/launch/launch.h"
17
18 #include <sstream>
19 #include <string>
20 #include <unordered_set>
21 #include <utility>
22 #include <vector>
23
24 #include <android-base/logging.h>
25 #include <fruit/fruit.h>
26
27 #include "common/libs/fs/shared_buf.h"
28 #include "common/libs/fs/shared_fd.h"
29 #include "common/libs/utils/files.h"
30 #include "common/libs/utils/result.h"
31 #include "host/commands/run_cvd/reporting.h"
32 #include "host/libs/config/command_source.h"
33 #include "host/libs/config/cuttlefish_config.h"
34 #include "host/libs/config/known_paths.h"
35 #include "host/libs/vm_manager/crosvm_manager.h"
36 #include "host/libs/vm_manager/qemu_manager.h"
37
38 namespace cuttlefish {
39
40 namespace {
41
CreateUnixInputServer(const std::string & path)42 SharedFD CreateUnixInputServer(const std::string& path) {
43 auto server =
44 SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
45 if (!server->IsOpen()) {
46 LOG(ERROR) << "Unable to create unix input server: " << server->StrError();
47 return {};
48 }
49 return server;
50 }
51
LaunchCustomActionServers(Command & webrtc_cmd,const std::vector<CustomActionServerConfig> & custom_actions)52 std::vector<Command> LaunchCustomActionServers(
53 Command& webrtc_cmd,
54 const std::vector<CustomActionServerConfig>& custom_actions) {
55 bool first = true;
56 std::vector<Command> commands;
57 for (const auto& custom_action : custom_actions) {
58 // Create a socket pair that will be used for communication between
59 // WebRTC and the action server.
60 SharedFD webrtc_socket, action_server_socket;
61 if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &webrtc_socket,
62 &action_server_socket)) {
63 LOG(ERROR) << "Unable to create custom action server socket pair: "
64 << strerror(errno);
65 continue;
66 }
67
68 // Launch the action server, providing its socket pair fd as the only
69 // argument.
70 auto binary = HostBinaryPath(custom_action.server);
71 Command command(binary);
72 command.AddParameter(action_server_socket);
73 commands.emplace_back(std::move(command));
74
75 // Pass the WebRTC socket pair fd to WebRTC.
76 if (first) {
77 first = false;
78 webrtc_cmd.AddParameter("-action_servers=", custom_action.server, ":",
79 webrtc_socket);
80 } else {
81 webrtc_cmd.AppendToLastParameter(",", custom_action.server, ":",
82 webrtc_socket);
83 }
84 }
85 return commands;
86 }
87
88 // Creates the frame and input sockets and add the relevant arguments to
89 // webrtc commands
90 class StreamerSockets : public virtual SetupFeature {
91 public:
INJECT(StreamerSockets (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))92 INJECT(StreamerSockets(const CuttlefishConfig& config,
93 const CuttlefishConfig::InstanceSpecific& instance))
94 : config_(config), instance_(instance) {}
95
AppendCommandArguments(Command & cmd)96 void AppendCommandArguments(Command& cmd) {
97 if (config_.vm_manager() == VmmMode::kQemu) {
98 cmd.AddParameter("-write_virtio_input");
99 }
100 if (!touch_servers_.empty()) {
101 bool is_chromeos =
102 instance_.boot_flow() ==
103 CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOs ||
104 instance_.boot_flow() ==
105 CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOsDisk;
106 if (is_chromeos) {
107 cmd.AddParameter("--multitouch=false");
108 }
109 cmd.AddParameter("-touch_fds=", touch_servers_[0]);
110 for (int i = 1; i < touch_servers_.size(); ++i) {
111 cmd.AppendToLastParameter(",", touch_servers_[i]);
112 }
113 }
114 if (instance_.enable_mouse()) {
115 cmd.AddParameter("-mouse_fd=", mouse_server_);
116 }
117 cmd.AddParameter("-rotary_fd=", rotary_server_);
118 cmd.AddParameter("-keyboard_fd=", keyboard_server_);
119 cmd.AddParameter("-frame_server_fd=", frames_server_);
120 if (instance_.enable_audio()) {
121 cmd.AddParameter("--audio_server_fd=", audio_server_);
122 }
123 cmd.AddParameter("--confui_in_fd=", confui_in_fd_);
124 cmd.AddParameter("--confui_out_fd=", confui_out_fd_);
125 cmd.AddParameter("--sensors_in_fd=", sensors_host_to_guest_fd_);
126 cmd.AddParameter("--sensors_out_fd=", sensors_guest_to_host_fd_);
127 }
128
129 // SetupFeature
Name() const130 std::string Name() const override { return "StreamerSockets"; }
Enabled() const131 bool Enabled() const override {
132 bool is_qemu = config_.vm_manager() == VmmMode::kQemu;
133 bool is_accelerated = instance_.gpu_mode() != kGpuModeGuestSwiftshader;
134 return !(is_qemu && is_accelerated);
135 }
136
137 private:
Dependencies() const138 std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
139
ResultSetup()140 Result<void> ResultSetup() override {
141 int display_cnt = instance_.display_configs().size();
142 int touchpad_cnt = instance_.touchpad_configs().size();
143 for (int i = 0; i < display_cnt + touchpad_cnt; ++i) {
144 SharedFD touch_socket =
145 CreateUnixInputServer(instance_.touch_socket_path(i));
146 CF_EXPECT(touch_socket->IsOpen(), touch_socket->StrError());
147 touch_servers_.emplace_back(std::move(touch_socket));
148 }
149 if (instance_.enable_mouse()) {
150 mouse_server_ = CreateUnixInputServer(instance_.mouse_socket_path());
151 CF_EXPECT(mouse_server_->IsOpen(), mouse_server_->StrError());
152 }
153 rotary_server_ =
154 CreateUnixInputServer(instance_.rotary_socket_path());
155
156 CF_EXPECT(rotary_server_->IsOpen(), rotary_server_->StrError());
157 keyboard_server_ = CreateUnixInputServer(instance_.keyboard_socket_path());
158 CF_EXPECT(keyboard_server_->IsOpen(), keyboard_server_->StrError());
159
160 frames_server_ = CreateUnixInputServer(instance_.frames_socket_path());
161 CF_EXPECT(frames_server_->IsOpen(), frames_server_->StrError());
162 // TODO(schuffelen): Make this a separate optional feature?
163 if (instance_.enable_audio()) {
164 auto path = config_.ForDefaultInstance().audio_server_path();
165 audio_server_ =
166 SharedFD::SocketLocalServer(path, false, SOCK_SEQPACKET, 0666);
167 CF_EXPECT(audio_server_->IsOpen(), audio_server_->StrError());
168 }
169 CF_EXPECT(InitializeVConsoles());
170 return {};
171 }
172
InitializeVConsoles()173 Result<void> InitializeVConsoles() {
174 std::vector<std::string> fifo_files = {
175 instance_.PerInstanceInternalPath("confui_fifo_vm.in"),
176 instance_.PerInstanceInternalPath("confui_fifo_vm.out"),
177 instance_.PerInstanceInternalPath("sensors_fifo_vm.in"),
178 instance_.PerInstanceInternalPath("sensors_fifo_vm.out"),
179 };
180 for (const auto& path : fifo_files) {
181 unlink(path.c_str());
182 }
183 std::vector<SharedFD> fds;
184 for (const auto& path : fifo_files) {
185 fds.emplace_back(CF_EXPECT(SharedFD::Fifo(path, 0660)));
186 }
187 confui_in_fd_ = fds[0];
188 confui_out_fd_ = fds[1];
189 sensors_host_to_guest_fd_ = fds[2];
190 sensors_guest_to_host_fd_ = fds[3];
191 return {};
192 }
193
194 const CuttlefishConfig& config_;
195 const CuttlefishConfig::InstanceSpecific& instance_;
196 std::vector<SharedFD> touch_servers_;
197 SharedFD mouse_server_;
198 SharedFD rotary_server_;
199 SharedFD keyboard_server_;
200 SharedFD frames_server_;
201 SharedFD audio_server_;
202 SharedFD confui_in_fd_; // host -> guest
203 SharedFD confui_out_fd_; // guest -> host
204 SharedFD sensors_host_to_guest_fd_;
205 SharedFD sensors_guest_to_host_fd_;
206 };
207
208 class WebRtcServer : public virtual CommandSource,
209 public DiagnosticInformation,
210 public KernelLogPipeConsumer {
211 public:
INJECT(WebRtcServer (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,StreamerSockets & sockets,KernelLogPipeProvider & log_pipe_provider,const CustomActionConfigProvider & custom_action_config,WebRtcController & webrtc_controller))212 INJECT(WebRtcServer(const CuttlefishConfig& config,
213 const CuttlefishConfig::InstanceSpecific& instance,
214 StreamerSockets& sockets,
215 KernelLogPipeProvider& log_pipe_provider,
216 const CustomActionConfigProvider& custom_action_config,
217 WebRtcController& webrtc_controller))
218 : config_(config),
219 instance_(instance),
220 sockets_(sockets),
221 log_pipe_provider_(log_pipe_provider),
222 custom_action_config_(custom_action_config),
223 webrtc_controller_(webrtc_controller) {}
224 // DiagnosticInformation
Diagnostics() const225 std::vector<std::string> Diagnostics() const override {
226 if (!Enabled() ||
227 !(config_.ForDefaultInstance().start_webrtc_sig_server() ||
228 config_.ForDefaultInstance().start_webrtc_sig_server_proxy())) {
229 // When WebRTC is enabled but an operator other than the one launched by
230 // run_cvd is used there is no way to know the url to which to point the
231 // browser to.
232 return {};
233 }
234 std::ostringstream out;
235 out << "Point your browser to https://localhost:"
236 << config_.sig_server_port() << " to interact with the device.";
237 return {out.str()};
238 }
239
240 // CommandSource
Commands()241 Result<std::vector<MonitorCommand>> Commands() override {
242 std::vector<MonitorCommand> commands;
243 if (instance_.start_webrtc_sig_server()) {
244 Command sig_server(WebRtcSigServerBinary());
245 sig_server.AddParameter("-assets_dir=", instance_.webrtc_assets_dir());
246 sig_server.AddParameter("-use_secure_http=",
247 config_.sig_server_secure() ? "true" : "false");
248 if (!config_.webrtc_certs_dir().empty()) {
249 sig_server.AddParameter("-certs_dir=", config_.webrtc_certs_dir());
250 }
251 sig_server.AddParameter("-http_server_port=", config_.sig_server_port());
252 commands.emplace_back(std::move(sig_server));
253 }
254
255 if (instance_.start_webrtc_sig_server_proxy()) {
256 Command sig_proxy(WebRtcSigServerProxyBinary());
257 sig_proxy.AddParameter("-server_port=", config_.sig_server_port());
258 commands.emplace_back(std::move(sig_proxy));
259 }
260
261 auto stopper = [webrtc_controller = webrtc_controller_]() mutable {
262 (void)webrtc_controller.SendStopRecordingCommand();
263 return StopperResult::kStopFailure;
264 };
265
266 Command webrtc(WebRtcBinary(), KillSubprocessFallback(stopper));
267
268 webrtc.AddParameter("-group_id=", instance_.group_id());
269
270 webrtc.UnsetFromEnvironment("http_proxy");
271 sockets_.AppendCommandArguments(webrtc);
272 if (config_.vm_manager() == VmmMode::kCrosvm) {
273 webrtc.AddParameter("-switches_fd=", switches_server_);
274 }
275 // Currently there is no way to ensure the signaling server will already
276 // have bound the socket to the port by the time the webrtc process runs
277 // (the common technique of doing it from the launcher is not possible here
278 // as the server library being used creates its own sockets). However, this
279 // issue is mitigated slightly by doing some retrying and backoff in the
280 // webrtc process when connecting to the websocket, so it shouldn't be an
281 // issue most of the time.
282 webrtc.AddParameter("--command_fd=", webrtc_controller_.GetClientSocket());
283 webrtc.AddParameter("-kernel_log_events_fd=", kernel_log_events_pipe_);
284 webrtc.AddParameter("-client_dir=",
285 DefaultHostArtifactsPath("usr/share/webrtc/assets"));
286
287 // TODO get from launcher params
288 const auto& actions =
289 custom_action_config_.CustomActionServers(instance_.id());
290 for (auto& action : LaunchCustomActionServers(webrtc, actions)) {
291 commands.emplace_back(std::move(action));
292 }
293 commands.emplace_back(std::move(webrtc));
294 return commands;
295 }
296
297 // SetupFeature
Enabled() const298 bool Enabled() const override {
299 return sockets_.Enabled() && instance_.enable_webrtc();
300 }
301
302 private:
Name() const303 std::string Name() const override { return "WebRtcServer"; }
Dependencies() const304 std::unordered_set<SetupFeature*> Dependencies() const override {
305 return {static_cast<SetupFeature*>(&sockets_),
306 static_cast<SetupFeature*>(&log_pipe_provider_),
307 static_cast<SetupFeature*>(&webrtc_controller_)};
308 }
309
ResultSetup()310 Result<void> ResultSetup() override {
311 if (config_.vm_manager() == VmmMode::kCrosvm) {
312 switches_server_ =
313 CreateUnixInputServer(instance_.switches_socket_path());
314 CF_EXPECT(switches_server_->IsOpen(), switches_server_->StrError());
315 }
316 kernel_log_events_pipe_ = log_pipe_provider_.KernelLogPipe();
317 CF_EXPECT(kernel_log_events_pipe_->IsOpen(),
318 kernel_log_events_pipe_->StrError());
319 return {};
320 }
321
322 const CuttlefishConfig& config_;
323 const CuttlefishConfig::InstanceSpecific& instance_;
324 StreamerSockets& sockets_;
325 KernelLogPipeProvider& log_pipe_provider_;
326 const CustomActionConfigProvider& custom_action_config_;
327 WebRtcController& webrtc_controller_;
328 SharedFD kernel_log_events_pipe_;
329 SharedFD switches_server_;
330 };
331
332 } // namespace
333
334 fruit::Component<
335 fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
336 const CuttlefishConfig::InstanceSpecific,
337 const CustomActionConfigProvider, WebRtcController>>
launchStreamerComponent()338 launchStreamerComponent() {
339 return fruit::createComponent()
340 .addMultibinding<CommandSource, WebRtcServer>()
341 .addMultibinding<DiagnosticInformation, WebRtcServer>()
342 .addMultibinding<KernelLogPipeConsumer, WebRtcServer>()
343 .addMultibinding<SetupFeature, StreamerSockets>()
344 .addMultibinding<SetupFeature, WebRtcServer>();
345 }
346
347 } // namespace cuttlefish
348