| // |
| // Copyright (C) 2020 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 <android-base/strings.h> |
| #include <gflags/gflags.h> |
| #include <signal.h> |
| #include <unistd.h> |
| |
| #include <limits> |
| |
| #include "common/libs/device_config/device_config.h" |
| #include "common/libs/fs/shared_buf.h" |
| #include "common/libs/fs/shared_fd.h" |
| #include "common/libs/utils/tee_logging.h" |
| #include "host/commands/modem_simulator/modem_simulator.h" |
| #include "host/libs/config/cuttlefish_config.h" |
| |
| // we can start multiple modems simultaneously; each modem |
| // will listent to one server fd for incoming sms/phone call |
| // there should be at least 1 valid fd |
| DEFINE_string(server_fds, "", "A comma separated list of file descriptors"); |
| DEFINE_int32(sim_type, 1, "Sim type: 1 for normal, 2 for CtsCarrierApiTestCases"); |
| |
| std::vector<cuttlefish::SharedFD> ServerFdsFromCmdline() { |
| // Validate the parameter |
| std::string fd_list = FLAGS_server_fds; |
| for (auto c: fd_list) { |
| if (c != ',' && (c < '0' || c > '9')) { |
| LOG(ERROR) << "Invalid file descriptor list: " << fd_list; |
| std::exit(1); |
| } |
| } |
| |
| auto fds = android::base::Split(fd_list, ","); |
| std::vector<cuttlefish::SharedFD> shared_fds; |
| for (auto& fd_str: fds) { |
| auto fd = std::stoi(fd_str); |
| auto shared_fd = cuttlefish::SharedFD::Dup(fd); |
| close(fd); |
| shared_fds.push_back(shared_fd); |
| } |
| |
| return shared_fds; |
| } |
| |
| int main(int argc, char** argv) { |
| ::android::base::InitLogging(argv, android::base::StderrLogger); |
| google::ParseCommandLineFlags(&argc, &argv, false); |
| |
| // Modem simulator log saved in cuttlefish_runtime |
| auto config = cuttlefish::CuttlefishConfig::Get(); |
| auto instance = config->ForDefaultInstance(); |
| |
| auto modem_log_path = instance.PerInstancePath("modem_simulator.log"); |
| |
| { |
| auto log_path = instance.launcher_log_path(); |
| std::vector<std::string> log_files{log_path, modem_log_path}; |
| android::base::SetLogger(cuttlefish::LogToStderrAndFiles(log_files)); |
| } |
| |
| LOG(INFO) << "Start modem simulator, server_fds: " << FLAGS_server_fds |
| << ", Sim type: " << ((FLAGS_sim_type == 2) ? |
| "special for CtsCarrierApiTestCases" : "normal" ); |
| |
| auto server_fds = ServerFdsFromCmdline(); |
| if (server_fds.empty()) { |
| LOG(ERROR) << "Need to provide server fd"; |
| return -1; |
| } |
| |
| cuttlefish::NvramConfig::InitNvramConfigService(server_fds.size(), FLAGS_sim_type); |
| |
| // Don't get a SIGPIPE from the clients |
| if (sigaction(SIGPIPE, nullptr, nullptr) != 0) { |
| LOG(ERROR) << "Failed to set SIGPIPE to be ignored: " << strerror(errno); |
| } |
| |
| auto nvram_config = cuttlefish::NvramConfig::Get(); |
| auto nvram_config_file = nvram_config->ConfigFileLocation(); |
| |
| // Start channel monitor, wait for RIL to connect |
| int32_t modem_id = 0; |
| std::vector<std::shared_ptr<cuttlefish::ModemSimulator>> modem_simulators; |
| |
| for (auto& fd : server_fds) { |
| CHECK(fd->IsOpen()) << "Error creating or inheriting modem simulator server: " |
| << fd->StrError(); |
| |
| auto modem_simulator = std::make_shared<cuttlefish::ModemSimulator>(modem_id); |
| auto channel_monitor = |
| std::make_unique<cuttlefish::ChannelMonitor>(modem_simulator.get(), fd); |
| |
| modem_simulator->Initialize(std::move(channel_monitor)); |
| |
| modem_simulators.push_back(modem_simulator); |
| |
| modem_id++; |
| } |
| |
| // Monitor exit request and |
| // remote call, remote sms from other cuttlefish instance |
| std::string monitor_socket_name = "modem_simulator"; |
| std::stringstream ss; |
| ss << instance.host_port(); |
| monitor_socket_name.append(ss.str()); |
| |
| auto monitor_socket = cuttlefish::SharedFD::SocketLocalServer( |
| monitor_socket_name.c_str(), true, SOCK_STREAM, 0666); |
| if (!monitor_socket->IsOpen()) { |
| LOG(ERROR) << "Unable to create monitor socket for modem simulator"; |
| std::exit(cuttlefish::kServerError); |
| } |
| |
| // Server loop |
| while (true) { |
| cuttlefish::SharedFDSet read_set; |
| read_set.Set(monitor_socket); |
| int num_fds = cuttlefish::Select(&read_set, nullptr, nullptr, nullptr); |
| if (num_fds <= 0) { // Ignore select error |
| LOG(ERROR) << "Select call returned error : " << strerror(errno); |
| } else if (read_set.IsSet(monitor_socket)) { |
| auto conn = cuttlefish::SharedFD::Accept(*monitor_socket); |
| std::string buf(4, ' '); |
| auto read = cuttlefish::ReadExact(conn, &buf); |
| if (read <= 0) { |
| conn->Close(); |
| LOG(WARNING) << "Detected close from the other side"; |
| continue; |
| } |
| if (buf == "STOP") { // Exit request from parent process |
| LOG(INFO) << "Exit request from parent process"; |
| nvram_config->SaveToFile(nvram_config_file); |
| for (auto modem : modem_simulators) { |
| modem->SaveModemState(); |
| } |
| cuttlefish::WriteAll(conn, "OK"); // Ignore the return value. Exit anyway. |
| std::exit(cuttlefish::kSuccess); |
| } else if (buf.compare(0, 3, "REM") == 0) { // REMO for modem id 0 ... |
| // Remote request from other cuttlefish instance |
| int id = std::stoi(buf.substr(3, 1)); |
| if (id >= modem_simulators.size()) { |
| LOG(ERROR) << "Not supported modem simulator count: " << id; |
| } else { |
| modem_simulators[id]->SetRemoteClient(conn, true); |
| } |
| } |
| } |
| } |
| // Until kill or exit |
| } |