blob: b48a777fef4ca6d8f238791dfaebac03d5dc01fb [file] [log] [blame]
//
// 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