blob: 1a9eaaeb472a15b9c5e046a590c07f017c873f00 [file] [log] [blame]
// Copyright 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "wifi/wifi_facade.h"
#include <memory>
#include <optional>
#include "netsim-daemon/src/ffi.rs.h"
#include "netsim/config.pb.h"
#include "netsim/hci_packet.pb.h"
#include "rust/cxx.h"
#include "util/log.h"
#ifdef NETSIM_ANDROID_EMULATOR
#include "android-qemu2-glue/emulation/WifiService.h"
#endif
namespace netsim::wifi {
namespace {
class ChipInfo {
public:
std::shared_ptr<model::Chip::Radio> model;
ChipInfo(std::shared_ptr<model::Chip::Radio> model)
: model(std::move(model)) {}
};
std::unordered_map<uint32_t, std::shared_ptr<ChipInfo>> id_to_chip_info_;
#ifdef NETSIM_ANDROID_EMULATOR
std::shared_ptr<android::qemu2::WifiService> wifi_service;
#endif
;
bool ChangedState(model::State a, model::State b) {
return (b != model::State::UNKNOWN && a != b);
}
std::optional<model::State> GetState(uint32_t id) {
if (auto it = id_to_chip_info_.find(id); it != id_to_chip_info_.end()) {
auto &model = it->second->model;
return model->state();
}
BtsLogWarn("Failed to get WiFi state with unknown chip_id %d", id);
return std::nullopt;
}
void IncrTx(uint32_t id) {
if (auto it = id_to_chip_info_.find(id); it != id_to_chip_info_.end()) {
auto &model = it->second->model;
model->set_tx_count(model->tx_count() + 1);
}
}
void IncrRx(uint32_t id) {
if (auto it = id_to_chip_info_.find(id); it != id_to_chip_info_.end()) {
auto &model = it->second->model;
model->set_rx_count(model->rx_count() + 1);
}
}
} // namespace
namespace facade {
void Reset(uint32_t id) {
BtsLog("wifi::facade::Reset(%d)", id);
if (auto it = id_to_chip_info_.find(id); it != id_to_chip_info_.end()) {
auto chip_info = it->second;
chip_info->model->set_state(model::State::ON);
chip_info->model->set_tx_count(0);
chip_info->model->set_rx_count(0);
}
}
void Remove(uint32_t id) {
BtsLog("wifi::facade::Remove(%d)", id);
id_to_chip_info_.erase(id);
}
void Patch(uint32_t id, const model::Chip::Radio &request) {
BtsLog("wifi::facade::Patch(%d)", id);
auto it = id_to_chip_info_.find(id);
if (it == id_to_chip_info_.end()) {
BtsLogWarn("Patch an unknown chip_id: %d", id);
return;
}
auto &model = it->second->model;
if (ChangedState(model->state(), request.state())) {
model->set_state(request.state());
}
}
model::Chip::Radio Get(uint32_t id) {
BtsLog("wifi::facade::Get(%d)", id);
model::Chip::Radio radio;
if (auto it = id_to_chip_info_.find(id); it != id_to_chip_info_.end()) {
radio.CopyFrom(*it->second->model);
}
return radio;
}
void PatchCxx(uint32_t id,
const rust::Slice<::std::uint8_t const> proto_bytes) {
model::Chip::Radio radio;
radio.ParseFromArray(proto_bytes.data(), proto_bytes.size());
Patch(id, radio);
}
rust::Vec<uint8_t> GetCxx(uint32_t id) {
auto radio = Get(id);
std::vector<uint8_t> proto_bytes(radio.ByteSizeLong());
radio.SerializeToArray(proto_bytes.data(), proto_bytes.size());
rust::Vec<uint8_t> proto_rust_bytes;
std::copy(proto_bytes.begin(), proto_bytes.end(),
std::back_inserter(proto_rust_bytes));
return proto_rust_bytes;
}
void Add(uint32_t chip_id) {
BtsLog("wifi::facade::Add(%d)", chip_id);
auto model = std::make_shared<model::Chip::Radio>();
model->set_state(model::State::ON);
id_to_chip_info_.emplace(chip_id, std::make_shared<ChipInfo>(model));
}
size_t HandleWifiCallback(const uint8_t *buf, size_t size) {
// Broadcast the response to all WiFi chips.
std::vector<uint8_t> packet(buf, buf + size);
for (auto [chip_id, chip_info] : id_to_chip_info_) {
if (auto state = chip_info->model->state();
!state || state == model::State::OFF) {
continue;
}
netsim::wifi::IncrRx(chip_id);
echip::HandleResponse(chip_id, packet,
packet::HCIPacket::HCI_PACKET_UNSPECIFIED);
}
return size;
}
void Start(const rust::Slice<::std::uint8_t const> proto_bytes) {
#ifdef NETSIM_ANDROID_EMULATOR
// Initialize hostapd and slirp inside WiFi Service.
config::WiFi config;
config.ParseFromArray(proto_bytes.data(), proto_bytes.size());
android::qemu2::HostapdOptions hostapd = {
.disabled = config.hostapd_options().disabled(),
.ssid = config.hostapd_options().ssid(),
.passwd = config.hostapd_options().passwd()};
android::qemu2::SlirpOptions slirpOpts = {
.disabled = config.slirp_options().disabled(),
.ipv4 = (config.slirp_options().has_ipv4() ? config.slirp_options().ipv4()
: true),
.restricted = config.slirp_options().restricted(),
.vnet = config.slirp_options().vnet(),
.vhost = config.slirp_options().vhost(),
.vmask = config.slirp_options().vmask(),
.ipv6 = (config.slirp_options().has_ipv6() ? config.slirp_options().ipv6()
: true),
.vprefix6 = config.slirp_options().vprefix6(),
.vprefixLen = (uint8_t)config.slirp_options().vprefixlen(),
.vhost6 = config.slirp_options().vhost6(),
.vhostname = config.slirp_options().vhostname(),
.tftpath = config.slirp_options().tftpath(),
.bootfile = config.slirp_options().bootfile(),
.dhcpstart = config.slirp_options().dhcpstart(),
.dns = config.slirp_options().dns(),
.dns6 = config.slirp_options().dns6(),
};
auto builder = android::qemu2::WifiService::Builder()
.withHostapd(hostapd)
.withSlirp(slirpOpts)
.withOnReceiveCallback(HandleWifiCallback)
.withVerboseLogging(true);
wifi_service = builder.build();
if (!wifi_service->init()) {
BtsLogWarn("Failed to initialize wifi service");
}
#endif
}
void Stop() {
#ifdef NETSIM_ANDROID_EMULATOR
wifi_service->stop();
#endif
}
} // namespace facade
void HandleWifiRequest(uint32_t chip_id,
const std::shared_ptr<std::vector<uint8_t>> &packet) {
#ifdef NETSIM_ANDROID_EMULATOR
if (auto state = GetState(chip_id); !state || state == model::State::OFF) {
return;
}
netsim::wifi::IncrTx(chip_id);
// Send the packet to the WiFi service.
struct iovec iov[1];
iov[0].iov_base = packet->data();
iov[0].iov_len = packet->size();
wifi_service->send(android::base::IOVector(iov, iov + 1));
#endif
}
void HandleWifiRequestCxx(uint32_t chip_id, const rust::Vec<uint8_t> &packet) {
std::vector<uint8_t> buffer(packet.begin(), packet.end());
auto packet_ptr = std::make_shared<std::vector<uint8_t>>(buffer);
HandleWifiRequest(chip_id, packet_ptr);
}
} // namespace netsim::wifi