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 cmd(NetsimdBinary());
92 cmd.AddParameter("-s");
93 AddDevicesParameter(cmd);
94 // Release SharedFDs, they've been duped by Command
95 devices_.clear();
96 // Bluetooth controller properties file
97 cmd.AddParameter("--rootcanal_controller_properties_file=",
98 config_.rootcanal_config_file());
99 // Default commands file
100 cmd.AddParameter("--rootcanal_default_commands_file=",
101 config_.rootcanal_default_commands_file());
102 std::vector<MonitorCommand> commands;
103 commands.emplace_back(std::move(cmd));
104 return commands;
105 }
106
107 // Convert devices_ to json for netsimd -s <arg>. The devices_, created and
108 // validated during ResultSetup, contains all the SharedFDs and meta-data.
109
AddDevicesParameter(Command & c)110 void AddDevicesParameter(Command& c) {
111 c.AddParameter(R"({"devices":[)");
112 for (int i = 0; i < devices_.size(); ++i) {
113 devices_[i].Append(c);
114 if (devices_.size() - i > 1) {
115 c.AppendToLastParameter(",");
116 }
117 }
118 c.AppendToLastParameter("]}");
119 }
120
121 // SetupFeature
Name() const122 std::string Name() const override { return "Netsim"; }
Enabled() const123 bool Enabled() const override { return instance_.start_netsim(); }
124
125 private:
Dependencies() const126 std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
127
ResultSetup()128 Result<void> ResultSetup() {
129 auto netsimd = HostBinaryPath("netsimd");
130 CF_EXPECT(FileExists(netsimd),
131 "Failed to find netsimd binary: " << netsimd);
132
133 for (const auto& instance : config_.Instances()) {
134 Device device(instance.adb_ip_and_port());
135 // Add bluetooth chip if enabled
136 if (config_.netsim_radio_enabled(
137 CuttlefishConfig::NetsimRadio::Bluetooth)) {
138 Chip chip("BLUETOOTH");
139 CF_EXPECT(MakeFifo(instance, "bt_fifo_vm.in", chip.fd_in));
140 CF_EXPECT(MakeFifo(instance, "bt_fifo_vm.out", chip.fd_out));
141 device.chips.emplace_back(chip);
142 }
143 // Add other chips if enabled
144 devices_.emplace_back(device);
145 }
146 return {};
147 }
148
MakeFifo(const CuttlefishConfig::InstanceSpecific & instance,const char * relative_path,SharedFD & fd)149 Result<void> MakeFifo(const CuttlefishConfig::InstanceSpecific& instance,
150 const char* relative_path, SharedFD& fd) {
151 auto path = instance.PerInstanceInternalPath(relative_path);
152 unlink(path.c_str());
153 CF_EXPECT(mkfifo(path.c_str(), 0660) == 0,
154 "Failed to create fifo for Netsim: " << strerror(errno));
155
156 fd = SharedFD::Open(path, O_RDWR);
157
158 CF_EXPECT(fd->IsOpen(),
159 "Failed to open fifo for Netsim: " << fd->StrError());
160
161 return {};
162 }
163
164 private:
165 std::vector<Device> devices_;
166 const CuttlefishConfig& config_;
167 const CuttlefishConfig::InstanceSpecific& instance_;
168 };
169
170 } // namespace
171
172 fruit::Component<fruit::Required<const CuttlefishConfig,
173 const CuttlefishConfig::InstanceSpecific>>
NetsimServerComponent()174 NetsimServerComponent() {
175 return fruit::createComponent()
176 .addMultibinding<CommandSource, NetsimServer>()
177 .addMultibinding<SetupFeature, NetsimServer>();
178 }
179
180 } // namespace cuttlefish
181