| // |
| // 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 <android-base/strings.h> |
| |
| #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<CommandHandler> DataService::InitializeCommandHandlers() { |
| std::vector<CommandHandler> 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=[<state>[,<cid> OK |
| * [,<cid>[,...]]]] +CME ERROR: <err> |
| * +CGACT? [+CGACT: <cid>,<state>] |
| * [<CR><LF>+CGACT: <cid>,<state>[...]] |
| * <state>: integer type; indicates the state of PDP context activation. |
| * 0: deactivated |
| * 1: activated |
| * <cid>: (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<std::string> 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, <cid>. |
| * |
| * Command Possible response(s) |
| * +CGDCONT=[<cid>[,<PDP_type>[,<APN> OK |
| * [,<PDP_addr>[,<d_comp> [,<h_comp>] +CME ERROR: <err> |
| * ]]]]] |
| * +CGDCONT? +CGDCONT: <cid>,<pdp_type>,<APN>, |
| * <pdp_addr>,<d_comp>,<h_comp><CR><LF> |
| * [+CGDCONT: <cid>,<pdp_type>,<APN>, |
| * <pdp_addr>,<d_comp>,<h_comp><CR><LF>[...]] |
| * OK |
| * <cid>: see AT+CGACT |
| * <PDP_type>: string type; specifies the type of packet data protocol. |
| * Value: X.25, IP, IPV6, IPV4V6, OSPIH, PPP, Non-IP,Ethernet |
| * <APN>: 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 |
| * <PDP_addr>: string type; identifies the MT in the address space applicable |
| * to the PDP |
| * <d_comp>: integer type; controls PDP data compression |
| * <h_comp>: 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<std::string> 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[=<L2P>[,[,<cid> CONNECT |
| * [,...]]]] ERROR |
| * +CME ERROR: <err> |
| * |
| * <L2P>: 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 |
| * <cid>: 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 <cid>. |
| * |
| * Command Possible response(s) |
| * +CGCONTRDP[=<cid>] [+CGCONTRDP: <cid>,<bearer_id>,<apn> |
| * [,<local_addr and subnet_mask>[,<gw_addr> |
| * [,<DNS_prim_addr>[<DNS_sec_addr>[...]]]]]] |
| * [<CR><LF>+CGCONTRDP: <cid>,<bearer_id>,<apn> |
| * [,<local_addr and subnet_mask>[,<gw_addr> |
| * [,<DNS_prim_addr>[<DNS_sec_addr>[...]]]]]] |
| * |
| * <cid>: see AT+CGACT |
| * <bearer_id>: integer type; identifies the bearer, i.e. the EPS bearer and |
| * the NSAPI. |
| * <local_addr and subnet_mask>: string type; shows the IP address and subnet |
| * mask of the MT. |
| * <gw_addr>: string type; shows the Gateway Address of the MT. The string is |
| * given as dot-separated numeric (0-255) parameters. |
| * <DNS_prim_addr>: string type; shows the IP address of the primary DNS server. |
| * <DNS_sec_addr>: 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<std::string> 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 |