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