1 //
2 // Copyright (C) 2022 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 <string>
19 #include <unordered_set>
20 #include <utility>
21 #include <vector>
22
23 #include <fruit/fruit.h>
24
25 #include "common/libs/fs/shared_fd.h"
26 #include "common/libs/utils/files.h"
27 #include "common/libs/utils/result.h"
28 #include "host/libs/config/command_source.h"
29 #include "host/libs/config/known_paths.h"
30
31 namespace cuttlefish {
32 namespace {
33
34 // NetsimServer launches netsim server with fifos for radio HALs.
35 //
36 // netsimd -s '{devices:[
37 // {"name":"0.0.0.0:5000", "chips":[
38 // {"kind":"BLUETOOTH", "fdIn":10, "fdOut":11}]},
39 // {"name":"0.0.0.0:5010", "chips":[
40 // {"kind":"BLUETOOTH", "fdIn":14, "fdOut":15}]}]}
41
42 // Chip and Device classes pass SharedFD fifos between ResultSetup and Commands
43 // and format the netsim json command line.
44
45 class Chip {
46 public:
47 SharedFD fd_in;
48 SharedFD fd_out;
49
Chip(std::string kind)50 Chip(std::string kind) : kind_(kind) {}
51
52 // Append the chip information as Json to the command.
Append(Command & c) const53 void Append(Command& c) const {
54 c.AppendToLastParameter(R"({"kind":")", kind_, R"(","fdIn":)", fd_in,
55 R"(,"fdOut":)", fd_out, "}");
56 }
57
58 private:
59 std::string kind_;
60 };
61
62 class Device {
63 public:
Device(std::string name)64 Device(std::string name) : name_(name) {}
65
Append(Command & c) const66 void Append(Command& c) const {
67 c.AppendToLastParameter(R"({"name":")", name_, R"(","chips":[)");
68 for (int i = 0; i < chips.size(); ++i) {
69 chips[i].Append(c);
70 if (chips.size() - i > 1) {
71 c.AppendToLastParameter(",");
72 }
73 }
74 c.AppendToLastParameter("]}");
75 }
76
77 std::vector<Chip> chips;
78
79 private:
80 std::string name_;
81 };
82
83 class NetsimServer : public CommandSource {
84 public:
INJECT(NetsimServer (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))85 INJECT(NetsimServer(const CuttlefishConfig& config,
86 const CuttlefishConfig::InstanceSpecific& instance))
87 : config_(config), instance_(instance) {}
88
89 // CommandSource
Commands()90 Result<std::vector<MonitorCommand>> Commands() override {
91 Command netsimd(NetsimdBinary());
92 netsimd.AddParameter("-s");
93 AddDevicesParameter(netsimd);
94 // Release SharedFDs, they've been duped by Command
95 devices_.clear();
96 // Port configuration.
97 netsimd.AddParameter("--hci_port=", config_.rootcanal_hci_port());
98
99 // When no connector is requested, add the instance number
100 if (config_.netsim_connector_instance_num() ==
101 config_.netsim_instance_num()) {
102 // external instance numbers start at 1 not 0
103 netsimd.AddParameter("--instance_num=",
104 config_.netsim_instance_num() + 1);
105 } else {
106 // If instance_num is not the target, then inform netsim to forward
107 // packets to another netsim daemon that was launched from cuttlefish with
108 // a different instance_num.
109 netsimd.AddParameter("--connector_instance_num=",
110 config_.netsim_connector_instance_num() + 1);
111 }
112
113 // Add parameters from passthrough option --netsim-args.
114 for (auto const& arg : config_.netsim_args()) {
115 netsimd.AddParameter(arg);
116 }
117
118 // Add command for forwarding the HCI port to a vsock server.
119 Command hci_vsock_proxy(SocketVsockProxyBinary());
120 hci_vsock_proxy.AddParameter("--server_type=vsock");
121 hci_vsock_proxy.AddParameter("--server_vsock_port=",
122 config_.rootcanal_hci_port());
123 hci_vsock_proxy.AddParameter("--server_vsock_id=",
124 instance_.vsock_guest_cid());
125 hci_vsock_proxy.AddParameter("--client_type=tcp");
126 hci_vsock_proxy.AddParameter("--client_tcp_host=127.0.0.1");
127 hci_vsock_proxy.AddParameter("--client_tcp_port=",
128 config_.rootcanal_hci_port());
129
130 // Add command for forwarding the test port to a vsock server.
131 Command test_vsock_proxy(SocketVsockProxyBinary());
132 test_vsock_proxy.AddParameter("--server_type=vsock");
133 test_vsock_proxy.AddParameter("--server_vsock_port=",
134 config_.rootcanal_test_port());
135 test_vsock_proxy.AddParameter("--server_vsock_id=",
136 instance_.vsock_guest_cid());
137 test_vsock_proxy.AddParameter("--client_type=tcp");
138 test_vsock_proxy.AddParameter("--client_tcp_host=127.0.0.1");
139 test_vsock_proxy.AddParameter("--client_tcp_port=",
140 config_.rootcanal_test_port());
141
142 std::vector<MonitorCommand> commands;
143 commands.emplace_back(std::move(netsimd));
144 commands.emplace_back(std::move(hci_vsock_proxy));
145 commands.emplace_back(std::move(test_vsock_proxy));
146 return commands;
147 }
148
149 // Convert devices_ to json for netsimd -s <arg>. The devices_, created and
150 // validated during ResultSetup, contains all the SharedFDs and meta-data.
151
AddDevicesParameter(Command & c)152 void AddDevicesParameter(Command& c) {
153 c.AddParameter(R"({"devices":[)");
154 for (int i = 0; i < devices_.size(); ++i) {
155 devices_[i].Append(c);
156 if (devices_.size() - i > 1) {
157 c.AppendToLastParameter(",");
158 }
159 }
160 c.AppendToLastParameter("]}");
161 }
162
163 // SetupFeature
Name() const164 std::string Name() const override { return "Netsim"; }
Enabled() const165 bool Enabled() const override { return instance_.start_netsim(); }
166
167 private:
Dependencies() const168 std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
169
ResultSetup()170 Result<void> ResultSetup() {
171 auto netsimd = HostBinaryPath("netsimd");
172 CF_EXPECT(FileExists(netsimd),
173 "Failed to find netsimd binary: " << netsimd);
174
175 for (const auto& instance : config_.Instances()) {
176 Device device(instance.adb_ip_and_port());
177 // Add bluetooth chip if enabled
178 if (config_.netsim_radio_enabled(
179 CuttlefishConfig::NetsimRadio::Bluetooth)) {
180 Chip chip("BLUETOOTH");
181 chip.fd_in = CF_EXPECT(MakeFifo(instance, "bt_fifo_vm.in"));
182 chip.fd_out = CF_EXPECT(MakeFifo(instance, "bt_fifo_vm.out"));
183 device.chips.emplace_back(chip);
184 }
185 // Add uwb chip if enabled
186 if (config_.netsim_radio_enabled(CuttlefishConfig::NetsimRadio::Uwb)) {
187 Chip chip("UWB");
188 chip.fd_in = CF_EXPECT(MakeFifo(instance, "uwb_fifo_vm.in"));
189 chip.fd_out = CF_EXPECT(MakeFifo(instance, "uwb_fifo_vm.out"));
190 device.chips.emplace_back(chip);
191 }
192 // Add other chips if enabled
193 devices_.emplace_back(device);
194 }
195 return {};
196 }
197
MakeFifo(const CuttlefishConfig::InstanceSpecific & instance,const char * relative_path)198 Result<SharedFD> MakeFifo(const CuttlefishConfig::InstanceSpecific& instance,
199 const char* relative_path) {
200 auto path = instance.PerInstanceInternalPath(relative_path);
201 return CF_EXPECT(SharedFD::Fifo(path, 0660));
202 }
203
204 private:
205 std::vector<Device> devices_;
206 const CuttlefishConfig& config_;
207 const CuttlefishConfig::InstanceSpecific& instance_;
208 };
209
210 } // namespace
211
212 fruit::Component<fruit::Required<const CuttlefishConfig,
213 const CuttlefishConfig::InstanceSpecific>>
NetsimServerComponent()214 NetsimServerComponent() {
215 return fruit::createComponent()
216 .addMultibinding<CommandSource, NetsimServer>()
217 .addMultibinding<SetupFeature, NetsimServer>();
218 }
219
220 } // namespace cuttlefish
221