345 lines
12 KiB
C++
345 lines
12 KiB
C++
//
|
|
// 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_->Post(
|
|
makeSafeCallback(this, &DataService::updatePhysicalChannelconfigs,
|
|
modem_tech, freq, cellBandwidthDownlink, count),
|
|
std::chrono::seconds(1));
|
|
}
|
|
|
|
} // namespace cuttlefish
|