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 cmd.AddParameter("-rotary_fd=", rotary_server_);
115 cmd.AddParameter("-keyboard_fd=", keyboard_server_);
116 cmd.AddParameter("-frame_server_fd=", frames_server_);
117 if (instance_.enable_audio()) {
118 cmd.AddParameter("--audio_server_fd=", audio_server_);
119 }
120 cmd.AddParameter("--confui_in_fd=", confui_in_fd_);
121 cmd.AddParameter("--confui_out_fd=", confui_out_fd_);
122 cmd.AddParameter("--sensors_in_fd=", sensors_host_to_guest_fd_);
123 cmd.AddParameter("--sensors_out_fd=", sensors_guest_to_host_fd_);
124 }
125
126 // SetupFeature
Name() const127 std::string Name() const override { return "StreamerSockets"; }
Enabled() const128 bool Enabled() const override {
129 bool is_qemu = config_.vm_manager() == VmmMode::kQemu;
130 bool is_accelerated = instance_.gpu_mode() != kGpuModeGuestSwiftshader;
131 return !(is_qemu && is_accelerated);
132 }
133
134 private:
Dependencies() const135 std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
136
ResultSetup()137 Result<void> ResultSetup() override {
138 int display_cnt = instance_.display_configs().size();
139 int touchpad_cnt = instance_.touchpad_configs().size();
140 for (int i = 0; i < display_cnt + touchpad_cnt; ++i) {
141 SharedFD touch_socket =
142 CreateUnixInputServer(instance_.touch_socket_path(i));
143 CF_EXPECT(touch_socket->IsOpen(), touch_socket->StrError());
144 touch_servers_.emplace_back(std::move(touch_socket));
145 }
146 rotary_server_ =
147 CreateUnixInputServer(instance_.rotary_socket_path());
148
149 CF_EXPECT(rotary_server_->IsOpen(), rotary_server_->StrError());
150 keyboard_server_ = CreateUnixInputServer(instance_.keyboard_socket_path());
151 CF_EXPECT(keyboard_server_->IsOpen(), keyboard_server_->StrError());
152
153 frames_server_ = CreateUnixInputServer(instance_.frames_socket_path());
154 CF_EXPECT(frames_server_->IsOpen(), frames_server_->StrError());
155 // TODO(schuffelen): Make this a separate optional feature?
156 if (instance_.enable_audio()) {
157 auto path = config_.ForDefaultInstance().audio_server_path();
158 audio_server_ =
159 SharedFD::SocketLocalServer(path, false, SOCK_SEQPACKET, 0666);
160 CF_EXPECT(audio_server_->IsOpen(), audio_server_->StrError());
161 }
162 CF_EXPECT(InitializeVConsoles());
163 return {};
164 }
165
InitializeVConsoles()166 Result<void> InitializeVConsoles() {
167 std::vector<std::string> fifo_files = {
168 instance_.PerInstanceInternalPath("confui_fifo_vm.in"),
169 instance_.PerInstanceInternalPath("confui_fifo_vm.out"),
170 instance_.PerInstanceInternalPath("sensors_fifo_vm.in"),
171 instance_.PerInstanceInternalPath("sensors_fifo_vm.out"),
172 };
173 for (const auto& path : fifo_files) {
174 unlink(path.c_str());
175 }
176 std::vector<SharedFD> fds;
177 for (const auto& path : fifo_files) {
178 fds.emplace_back(CF_EXPECT(SharedFD::Fifo(path, 0660)));
179 }
180 confui_in_fd_ = fds[0];
181 confui_out_fd_ = fds[1];
182 sensors_host_to_guest_fd_ = fds[2];
183 sensors_guest_to_host_fd_ = fds[3];
184 return {};
185 }
186
187 const CuttlefishConfig& config_;
188 const CuttlefishConfig::InstanceSpecific& instance_;
189 std::vector<SharedFD> touch_servers_;
190 SharedFD rotary_server_;
191 SharedFD keyboard_server_;
192 SharedFD frames_server_;
193 SharedFD audio_server_;
194 SharedFD confui_in_fd_; // host -> guest
195 SharedFD confui_out_fd_; // guest -> host
196 SharedFD sensors_host_to_guest_fd_;
197 SharedFD sensors_guest_to_host_fd_;
198 };
199
200 class WebRtcServer : public virtual CommandSource,
201 public DiagnosticInformation,
202 public KernelLogPipeConsumer {
203 public:
INJECT(WebRtcServer (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,StreamerSockets & sockets,KernelLogPipeProvider & log_pipe_provider,const CustomActionConfigProvider & custom_action_config,WebRtcRecorder & webrtc_recorder))204 INJECT(WebRtcServer(const CuttlefishConfig& config,
205 const CuttlefishConfig::InstanceSpecific& instance,
206 StreamerSockets& sockets,
207 KernelLogPipeProvider& log_pipe_provider,
208 const CustomActionConfigProvider& custom_action_config,
209 WebRtcRecorder& webrtc_recorder))
210 : config_(config),
211 instance_(instance),
212 sockets_(sockets),
213 log_pipe_provider_(log_pipe_provider),
214 custom_action_config_(custom_action_config),
215 webrtc_recorder_(webrtc_recorder) {}
216 // DiagnosticInformation
Diagnostics() const217 std::vector<std::string> Diagnostics() const override {
218 if (!Enabled() ||
219 !(config_.ForDefaultInstance().start_webrtc_sig_server() ||
220 config_.ForDefaultInstance().start_webrtc_sig_server_proxy())) {
221 // When WebRTC is enabled but an operator other than the one launched by
222 // run_cvd is used there is no way to know the url to which to point the
223 // browser to.
224 return {};
225 }
226 std::ostringstream out;
227 out << "Point your browser to https://localhost:"
228 << config_.sig_server_port() << " to interact with the device.";
229 return {out.str()};
230 }
231
232 // CommandSource
Commands()233 Result<std::vector<MonitorCommand>> Commands() override {
234 std::vector<MonitorCommand> commands;
235 if (instance_.start_webrtc_sig_server()) {
236 Command sig_server(WebRtcSigServerBinary());
237 sig_server.AddParameter("-assets_dir=", instance_.webrtc_assets_dir());
238 sig_server.AddParameter("-use_secure_http=",
239 config_.sig_server_secure() ? "true" : "false");
240 if (!config_.webrtc_certs_dir().empty()) {
241 sig_server.AddParameter("-certs_dir=", config_.webrtc_certs_dir());
242 }
243 sig_server.AddParameter("-http_server_port=", config_.sig_server_port());
244 commands.emplace_back(std::move(sig_server));
245 }
246
247 if (instance_.start_webrtc_sig_server_proxy()) {
248 Command sig_proxy(WebRtcSigServerProxyBinary());
249 sig_proxy.AddParameter("-server_port=", config_.sig_server_port());
250 commands.emplace_back(std::move(sig_proxy));
251 }
252
253 auto stopper = [webrtc_recorder = webrtc_recorder_]() {
254 webrtc_recorder.SendStopRecordingCommand();
255 return StopperResult::kStopFailure;
256 };
257
258 Command webrtc(WebRtcBinary(), KillSubprocessFallback(stopper));
259
260 webrtc.AddParameter("-group_id=", instance_.group_id());
261
262 webrtc.UnsetFromEnvironment("http_proxy");
263 sockets_.AppendCommandArguments(webrtc);
264 if (config_.vm_manager() == VmmMode::kCrosvm) {
265 webrtc.AddParameter("-switches_fd=", switches_server_);
266 }
267 // Currently there is no way to ensure the signaling server will already
268 // have bound the socket to the port by the time the webrtc process runs
269 // (the common technique of doing it from the launcher is not possible here
270 // as the server library being used creates its own sockets). However, this
271 // issue is mitigated slightly by doing some retrying and backoff in the
272 // webrtc process when connecting to the websocket, so it shouldn't be an
273 // issue most of the time.
274 webrtc.AddParameter("--command_fd=", webrtc_recorder_.GetClientSocket());
275 webrtc.AddParameter("-kernel_log_events_fd=", kernel_log_events_pipe_);
276 webrtc.AddParameter("-client_dir=",
277 DefaultHostArtifactsPath("usr/share/webrtc/assets"));
278
279 // TODO get from launcher params
280 const auto& actions =
281 custom_action_config_.CustomActionServers(instance_.id());
282 for (auto& action : LaunchCustomActionServers(webrtc, actions)) {
283 commands.emplace_back(std::move(action));
284 }
285 commands.emplace_back(std::move(webrtc));
286 return commands;
287 }
288
289 // SetupFeature
Enabled() const290 bool Enabled() const override {
291 return sockets_.Enabled() && instance_.enable_webrtc();
292 }
293
294 private:
Name() const295 std::string Name() const override { return "WebRtcServer"; }
Dependencies() const296 std::unordered_set<SetupFeature*> Dependencies() const override {
297 return {static_cast<SetupFeature*>(&sockets_),
298 static_cast<SetupFeature*>(&log_pipe_provider_),
299 static_cast<SetupFeature*>(&webrtc_recorder_)};
300 }
301
ResultSetup()302 Result<void> ResultSetup() override {
303 if (config_.vm_manager() == VmmMode::kCrosvm) {
304 switches_server_ =
305 CreateUnixInputServer(instance_.switches_socket_path());
306 CF_EXPECT(switches_server_->IsOpen(), switches_server_->StrError());
307 }
308 kernel_log_events_pipe_ = log_pipe_provider_.KernelLogPipe();
309 CF_EXPECT(kernel_log_events_pipe_->IsOpen(),
310 kernel_log_events_pipe_->StrError());
311 return {};
312 }
313
314 const CuttlefishConfig& config_;
315 const CuttlefishConfig::InstanceSpecific& instance_;
316 StreamerSockets& sockets_;
317 KernelLogPipeProvider& log_pipe_provider_;
318 const CustomActionConfigProvider& custom_action_config_;
319 WebRtcRecorder& webrtc_recorder_;
320 SharedFD kernel_log_events_pipe_;
321 SharedFD switches_server_;
322 };
323
324 } // namespace
325
326 fruit::Component<
327 fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
328 const CuttlefishConfig::InstanceSpecific,
329 const CustomActionConfigProvider, WebRtcRecorder>>
launchStreamerComponent()330 launchStreamerComponent() {
331 return fruit::createComponent()
332 .addMultibinding<CommandSource, WebRtcServer>()
333 .addMultibinding<DiagnosticInformation, WebRtcServer>()
334 .addMultibinding<KernelLogPipeConsumer, WebRtcServer>()
335 .addMultibinding<SetupFeature, StreamerSockets>()
336 .addMultibinding<SetupFeature, WebRtcServer>();
337 }
338
339 } // namespace cuttlefish
340