// // 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 "host/commands/modem_simulator/data_service.h" #include #include "host/commands/modem_simulator/device_config.h" namespace cuttlefish { DataService::DataService(int32_t service_id, ChannelMonitor* channel_monitor, ThreadLooper* thread_looper) : ModemService(service_id, this->InitializeCommandHandlers(), channel_monitor, thread_looper) { InitializeServiceState(); } std::vector DataService::InitializeCommandHandlers() { std::vector command_handlers = { CommandHandler("+CGACT=", [this](const Client& client, std::string& cmd) { this->HandleActivateDataCall(client, cmd); }), CommandHandler("+CGACT?", [this](const Client& client) { this->HandleQueryDataCallList(client); }), CommandHandler("+CGDCONT=", [this](const Client& client, std::string& cmd) { this->HandlePDPContext(client, cmd); }), CommandHandler("+CGDCONT?", [this](const Client& client) { this->HandleQueryPDPContextList(client); }), CommandHandler("+CGQREQ=1", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CGQMIN=1", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CGEREP=1,0", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CGDATA", [this](const Client& client, std::string& cmd) { this->HandleEnterDataState(client, cmd); }), CommandHandler("D*99***1#", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CGCONTRDP", [this](const Client& client, std::string& cmd) { this->HandleReadDynamicParam(client, cmd); }), }; return (command_handlers); } void DataService::InitializeServiceState() { // Initialize data connection config } /** * AT+CGACT * The execution command is used to activate or deactivate the specified PDP * context(s). * * Command Possible response(s) * +CGACT=[[, OK * [,[,...]]]] +CME ERROR: * +CGACT? [+CGACT: ,] * [+CGACT: ,[...]] * : integer type; indicates the state of PDP context activation. * 0: deactivated * 1: activated * : (PDP Context Identifier) integer(1~15), specifies the PDP context ID. * * see RIL_REQUEST_SETUP_DATA_CALL in RIL */ void DataService::HandleActivateDataCall(const Client& client, const std::string& /*command*/) { client.SendCommandResponse("OK"); } /** * see AT+CGACT */ void DataService::HandleQueryDataCallList(const Client& client) { std::vector responses; std::stringstream ss; for (auto iter = pdp_context_.begin(); iter != pdp_context_.end(); ++iter) { if (iter->state == PDPContext::ACTIVE) { ss.clear(); ss << "+CGACT: " << iter->cid << "," << iter->state; responses.push_back(ss.str()); ss.str(""); } } responses.push_back("OK"); client.SendCommandResponse(responses); } /** * AT+CGDCONT * The set command specifies PDP context parameter values for a PDP context * identified by the (local) context identification parameter, . * * Command Possible response(s) * +CGDCONT=[[,[, OK * [,[, [,] +CME ERROR: * ]]]]] * +CGDCONT? +CGDCONT: ,,, * ,, * [+CGDCONT: ,,, * ,,[...]] * OK * : see AT+CGACT * : string type; specifies the type of packet data protocol. * Value: X.25, IP, IPV6, IPV4V6, OSPIH, PPP, Non-IP,Ethernet * : string type; a logical name that is used to select the GGSN or the * external packet data network.If the value is null or omitted, then * the subscription value will be requested * : string type; identifies the MT in the address space applicable * to the PDP * : integer type; controls PDP data compression * : integer type; controls PDP header compression * * see RIL_REQUEST_SETUP_DATA_CALL in RIL */ void DataService::HandlePDPContext(const Client& client, const std::string& command) { CommandParser cmd(command); cmd.SkipPrefix(); /* skip +CGDCONT= */ int cid = cmd.GetNextInt(); std::string ip_type(cmd.GetNextStr(',')); std::string apn(cmd.GetNextStr(',')); auto address = cuttlefish::modem::DeviceConfig::ril_address_and_prefix(); auto dnses = cuttlefish::modem::DeviceConfig::ril_dns(); auto gateways = cuttlefish::modem::DeviceConfig::ril_gateway(); PDPContext pdp_context = {cid, PDPContext::ACTIVE, ip_type, // IPV4 or IPV6 or IPV4V6 apn, address, dnses, gateways}; // check cid auto iter = pdp_context_.begin(); for (; iter != pdp_context_.end(); ++iter) { if (pdp_context.cid == iter->cid) { *iter = pdp_context; break; } } if (iter == pdp_context_.end()) { pdp_context_.push_back(pdp_context); } client.SendCommandResponse("OK"); } /** * see AT+CGDCONT above */ void DataService::HandleQueryPDPContextList(const Client& client) { std::vector responses; std::stringstream ss; for (auto it = pdp_context_.begin(); it != pdp_context_.end(); ++it) { std::stringstream ss; ss << "+CGDCONT: " << it->cid << "," << it->conn_types << "," << it->apn << "," << it->addresses << ",0,0"; responses.push_back(ss.str()); } responses.push_back("OK"); client.SendCommandResponse(responses); } /** * AT+CGDATA * The execution command causes the MT to perform whatever actions are * necessary to establish communication between the TE and the network using * one or more Packet Domain PDP types. * * Command Possible response(s) * +CGDATA[=[,[, CONNECT * [,...]]]] ERROR * +CME ERROR: * * : string type; indicates the layer 2 protocol to be used between the * TE and MT NULL none, for PDP type OSP:IHOSS (Obsolete) * value: PPP, PAD, X25, M-xxxx * : see AT+CGACT * * see RIL_REQUEST_SETUP_DATA_CALL in RIL */ void DataService::HandleEnterDataState(const Client& client, const std::string& command) { std::string response; CommandParser cmd(command); cmd.SkipPrefix(); cmd.SkipComma(); int cid = cmd.GetNextInt(); // Check cid auto iter = pdp_context_.begin(); for (; iter != pdp_context_.end(); ++iter) { if (cid == iter->cid && iter->state == PDPContext::ACTIVE) { response = "CONNECT"; break; } } if (iter == pdp_context_.end()) { response = "ERROR"; } client.SendCommandResponse(response); } /** * AT+CGCONTRDP * The execution command returns the relevant information for an active non * secondary PDP context with the context identifier . * * Command Possible response(s) * +CGCONTRDP[=] [+CGCONTRDP: ,, * [,[, * [,[[...]]]]]] * [+CGCONTRDP: ,, * [,[, * [,[[...]]]]]] * * : see AT+CGACT * : integer type; identifies the bearer, i.e. the EPS bearer and * the NSAPI. * : string type; shows the IP address and subnet * mask of the MT. * : string type; shows the Gateway Address of the MT. The string is * given as dot-separated numeric (0-255) parameters. * : string type; shows the IP address of the primary DNS server. * : string type; shows the IP address of the secondary DNS server. * * * see RIL_REQUEST_SETUP_DATA_CALL in RIL */ void DataService::HandleReadDynamicParam(const Client& client, const std::string& command) { std::vector responses; CommandParser cmd(command); cmd.SkipPrefix(); /* skip prefix AT+CGCONTRDP= */ int cid = cmd.GetNextInt(); auto iter = pdp_context_.begin(); // Check cid for (; iter != pdp_context_.end(); ++iter) { if (cid == iter->cid && iter->state == PDPContext::ACTIVE) { break; } } if (iter == pdp_context_.end()) { responses.push_back(kCmeErrorInvalidIndex); // number } else { std::stringstream ss; ss << "+CGCONTRDP: " << iter->cid << ",5," << iter->apn << "," << iter->addresses << "," << iter->gateways << "," << iter->dnses; responses.push_back(ss.str()); responses.push_back("OK"); } client.SendCommandResponse(responses); } void DataService::sendOnePhysChanCfgUpdate(int status, int bandwidth, int rat, int freq, int id) { std::stringstream ss; ss << "%CGFPCCFG: " << status << "," << bandwidth << "," << rat << "," << freq << "," << id; SendUnsolicitedCommand(ss.str()); } void DataService::onUpdatePhysicalChannelconfigs(int modem_tech, int freq, int cellBandwidthDownlink) { updatePhysicalChannelconfigs(modem_tech, freq, cellBandwidthDownlink, 3); } void DataService::updatePhysicalChannelconfigs(int modem_tech, int freq, int cellBandwidthDownlink, int count) { if (count <= 0) { return; } const int PRIMARY_SERVING = 1; const int SECONDARY_SERVING = 2; for (const auto& iter : pdp_context_) { if (iter.state == PDPContext::ACTIVE) { sendOnePhysChanCfgUpdate(PRIMARY_SERVING, cellBandwidthDownlink, modem_tech, freq, iter.cid); sendOnePhysChanCfgUpdate(SECONDARY_SERVING, cellBandwidthDownlink, modem_tech, freq, iter.cid); } } // call again after 1 sec delay count--; thread_looper_->PostWithDelay( std::chrono::seconds(1), makeSafeCallback(this, &DataService::updatePhysicalChannelconfigs, modem_tech, freq, cellBandwidthDownlink, count)); } } // namespace cuttlefish