389 lines
13 KiB
C++
389 lines
13 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/sms_service.h"
|
||
|
||
#include <android-base/logging.h>
|
||
|
||
#include "host/commands/modem_simulator/pdu_parser.h"
|
||
|
||
namespace cuttlefish {
|
||
|
||
SmsService::SmsService(int32_t service_id, ChannelMonitor* channel_monitor,
|
||
ThreadLooper* thread_looper)
|
||
: ModemService(service_id, this->InitializeCommandHandlers(),
|
||
channel_monitor, thread_looper) {
|
||
InitializeServiceState();
|
||
}
|
||
|
||
std::vector<CommandHandler> SmsService::InitializeCommandHandlers() {
|
||
std::vector<CommandHandler> command_handlers = {
|
||
CommandHandler("+CMGS",
|
||
[this](const Client& client, std::string& cmd) {
|
||
this->HandleSendSMS(client, cmd);
|
||
}),
|
||
CommandHandler("+CNMA",
|
||
[this](const Client& client, std::string& cmd) {
|
||
this->HandleSMSAcknowledge(client, cmd);
|
||
}),
|
||
CommandHandler("+CMGW",
|
||
[this](const Client& client, std::string& cmd) {
|
||
this->HandleWriteSMSToSim(client, cmd);
|
||
}),
|
||
CommandHandler("+CMGD",
|
||
[this](const Client& client, std::string& cmd) {
|
||
this->HandleDeleteSmsOnSim(client, cmd);
|
||
}),
|
||
CommandHandler("+CSCB",
|
||
[this](const Client& client, std::string& cmd) {
|
||
this->HandleBroadcastConfig(client, cmd);
|
||
}),
|
||
CommandHandler(
|
||
"+CSCA?",
|
||
[this](const Client& client) { this->HandleGetSmscAddress(client); }),
|
||
CommandHandler("+CSCA=",
|
||
[this](const Client& client, std::string& cmd) {
|
||
this->HandleSetSmscAddress(client, cmd);
|
||
}),
|
||
CommandHandler("+REMOTESMS",
|
||
[this](const Client& client, std::string& cmd) {
|
||
this->HandleReceiveRemoteSMS(client, cmd);
|
||
}),
|
||
};
|
||
return (command_handlers);
|
||
}
|
||
|
||
void SmsService::InitializeServiceState() {
|
||
is_waiting_sms_pdu_ = false;
|
||
is_waiting_sms_to_sim_ = false;
|
||
message_id_ = 1;
|
||
message_reference_ = 1;
|
||
|
||
broadcast_config_ = {0, "", ""};
|
||
}
|
||
|
||
void SmsService::SetupDependency(SimService* sim) { sim_service_ = sim; }
|
||
|
||
/**
|
||
* AT+CMGS
|
||
* This command sends message from a TE to the network (SMS-SUBMIT).
|
||
*
|
||
* Command Possible response(s)
|
||
* +CMGS=<length><CR> "> "
|
||
* PDU is given<ctrl-Z/ESC> +CMGS: <mr>[,<ackpdu>]<CR>OK
|
||
* +CMS ERROR: <err>
|
||
*
|
||
* <length>:must indicate the number of octets coded in the TP
|
||
* layer data unit to be given.
|
||
*
|
||
* see RIL_REQUEST_SEND_SMS in RIL
|
||
*/
|
||
void SmsService::HandleSendSMS(const Client& client, std::string& /*command*/) {
|
||
is_waiting_sms_pdu_ = true;
|
||
|
||
std::vector<std::string> responses;
|
||
responses.push_back("> ");
|
||
client.SendCommandResponse(responses);
|
||
}
|
||
|
||
/**
|
||
* AT+CNMA
|
||
* This command confirms reception of a new message (SMS-DELIVER or
|
||
* SMS-STATUS-REPORT) which is routed directly to the TE.
|
||
*
|
||
* Command Possible response(s)
|
||
* +CNMA [=<n>[, <length> [<CR> OK
|
||
* PDU is given<ctrl-Z/ESC>]]] +CMS ERROR: <err>
|
||
*
|
||
* <n>: integer type
|
||
* 0: command operates similarly as defined for the text mode
|
||
* 1: send RP-ACK
|
||
* 2: send RP-ERROR
|
||
* <length>: ACKPDU length(octet)
|
||
*
|
||
* see RIL_REQUEST_SMS_ACKNOWLEDGE in RIL
|
||
*/
|
||
void SmsService::HandleSMSAcknowledge(const Client& client, std::string& /*command*/) {
|
||
client.SendCommandResponse("OK");
|
||
}
|
||
|
||
/*
|
||
* AT+CMGW
|
||
* This command stores message (either SMS-DELIVER or SMS-SUBMIT)
|
||
* to memory storage <mem2>.
|
||
*
|
||
* Command Possible response(s)
|
||
* +CMGW=<length>[,<stat>]<CR> "> "
|
||
* PDU is given <ctrl-Z/ESC> +CMGW: <index>
|
||
* +CMS ERROR: <err>
|
||
* <length>: the length of TPDU(bit) with a range of 9-160
|
||
* < stat >: integer:
|
||
* 0: Unread Message. (MT)
|
||
* 1: Read Message. (MT)
|
||
* 2: Unsent Message. (MO)
|
||
* 3: Sent Message. (MO)
|
||
* < index>: index id of <mem2>
|
||
*
|
||
* see RIL_REQUEST_WRITE_SMS_TO_SIM in RIL
|
||
*/
|
||
void SmsService::HandleWriteSMSToSim(const Client& client, std::string& command) {
|
||
is_waiting_sms_to_sim_ = true;
|
||
|
||
CommandParser cmd(command);
|
||
cmd.SkipPrefix(); // skip "AT+CMGW="
|
||
cmd.SkipComma();
|
||
sms_status_on_sim_ = (SmsMessage::SmsStatus)cmd.GetNextInt();
|
||
client.SendCommandResponse("> ");
|
||
}
|
||
|
||
/**
|
||
* AT+CMGD
|
||
* This command deletes message from preferred message storage <mem1>
|
||
* location <index>.
|
||
*
|
||
* Command Possible response(s)
|
||
* +CMGD=<index>[, <DelFlag>] OK
|
||
* +CMS ERROR: <err>
|
||
* < index>: index id of <mem2>
|
||
*
|
||
* see RIL_REQUEST_DELETE_SMS_ON_SIM in RIL
|
||
*/
|
||
void SmsService::HandleDeleteSmsOnSim(const Client& client, std::string& command) {
|
||
CommandParser cmd(command);
|
||
cmd.SkipPrefix(); // skip "AT+CMGD="
|
||
|
||
int index = cmd.GetNextInt();
|
||
auto iter = messages_on_sim_card_.find(index);
|
||
if (iter == messages_on_sim_card_.end()) {
|
||
client.SendCommandResponse(kCmeErrorInvalidIndex); // No such message
|
||
return;
|
||
}
|
||
|
||
messages_on_sim_card_.erase(iter);
|
||
client.SendCommandResponse("OK");
|
||
}
|
||
|
||
/**
|
||
* AT+CSCB
|
||
* Set command selects which types of CBMs are to be received by the ME.
|
||
*
|
||
* Command Possible response(s)
|
||
* +CSCB=[<mode>[,<mids>[,<dcss>]]] OK
|
||
* +CSCB? +CSCB: <mode>,<mids>,<dcss>
|
||
*
|
||
* <mode>:
|
||
* 0: message types specified in <mids> and <dcss> are accepted
|
||
* 1: message types specified in <mids> and <dcss> are not accepted
|
||
* <mids>: string type; all different possible combinations of CBM message
|
||
* identifiers (refer <mid>).
|
||
* <dcss>: string type; all different possible combinations of CBM data coding
|
||
* schemes.
|
||
*
|
||
* see RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG &
|
||
* RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG in RIL
|
||
* Notes: This command is allowed in TEXT mode.
|
||
*/
|
||
void SmsService::HandleBroadcastConfig(const Client& client, std::string& command) {
|
||
std::vector<std::string> responses;
|
||
|
||
CommandParser cmd(command);
|
||
cmd.SkipPrefix();
|
||
if (*cmd == "AT+CSCB?") { // Query
|
||
std::stringstream ss;
|
||
ss << "+CSCB: " << broadcast_config_.mode << ","
|
||
<< broadcast_config_.mids << ","
|
||
<< broadcast_config_.dcss;
|
||
responses.push_back(ss.str());
|
||
} else { // Set
|
||
broadcast_config_.mode = cmd.GetNextInt();
|
||
broadcast_config_.mids = cmd.GetNextStr();
|
||
broadcast_config_.dcss = cmd.GetNextStr();
|
||
}
|
||
responses.push_back("OK");
|
||
client.SendCommandResponse(responses);
|
||
}
|
||
|
||
/**
|
||
* AT+CSCA
|
||
* Set command updates the SMSC address, through which mobile originated
|
||
* SMs are transmitted.
|
||
*
|
||
* Command Possible response(s)
|
||
* +CSCA=<sca>[,<tosca>] OK
|
||
* +CSCA? +CSCA: <sca>,<tosca>
|
||
*
|
||
* <sca>: service center address, its maximum length is 20.
|
||
* <tosca>: service center address format,protocol uses 8-bit address integer.
|
||
*
|
||
* see RIL_REQUEST_SET_SMSC_ADDRESS in RIL
|
||
*/
|
||
void SmsService::HandleGetSmscAddress(const Client& client) {
|
||
std::vector<std::string> responses;
|
||
|
||
std::stringstream ss;
|
||
ss << "+CSCA: " << sms_service_center_address_.sca << ","
|
||
<< sms_service_center_address_.tosca;
|
||
responses.push_back(ss.str());
|
||
responses.push_back("OK");
|
||
client.SendCommandResponse(responses);
|
||
}
|
||
|
||
void SmsService::HandleSetSmscAddress(const Client& client, std::string& command) {
|
||
CommandParser cmd(command);
|
||
cmd.SkipPrefix(); // skip "AT+CSCA="
|
||
|
||
sms_service_center_address_.sca = cmd.GetNextStr();
|
||
sms_service_center_address_.tosca = cmd.GetNextInt();
|
||
|
||
client.SendCommandResponse("OK");
|
||
}
|
||
|
||
void SmsService::SendSmsToRemote(std::string remote_port, PDUParser& sms_pdu) {
|
||
auto remote_client = ConnectToRemoteCvd(remote_port);
|
||
if (!remote_client->IsOpen()) {
|
||
return;
|
||
}
|
||
|
||
auto local_host_id = GetHostId();
|
||
auto pdu = sms_pdu.CreateRemotePDU(local_host_id);
|
||
|
||
std::string command = "AT+REMOTESMS=" + pdu + "\r";
|
||
std::string token = "REM0";
|
||
remote_client->Write(token.data(), token.size());
|
||
remote_client->Write(command.data(), command.size());
|
||
}
|
||
|
||
/* process AT+CMGS PDU */
|
||
void SmsService::HandleSendSMSPDU(const Client& client, std::string& command) {
|
||
is_waiting_sms_pdu_ = false;
|
||
|
||
std::vector<std::string> responses;
|
||
PDUParser sms_pdu(command);
|
||
if (!sms_pdu.IsValidPDU()) {
|
||
/* Invalid PDU mode parameter */
|
||
client.SendCommandResponse(kCmsErrorInvalidPDUModeParam);
|
||
return;
|
||
}
|
||
|
||
std::string phone_number = sms_pdu.GetPhoneNumberFromAddress();
|
||
|
||
int port = 0;
|
||
if (phone_number.length() == 11) {
|
||
port = std::stoi(phone_number.substr(7));
|
||
} else if (phone_number.length() == 4) {
|
||
port = std::stoi(phone_number);
|
||
}
|
||
|
||
if (phone_number == "") { /* Phone number unknown */
|
||
LOG(ERROR) << "Failed to get phone number form address";
|
||
client.SendCommandResponse(kCmsErrorSCAddressUnknown);
|
||
return;
|
||
} else if (port >= kRemotePortRange.first &&
|
||
port <= kRemotePortRange.second) {
|
||
auto remote_host_port = std::to_string(port);
|
||
if (GetHostId() == remote_host_port) { // Send SMS to local host port
|
||
thread_looper_->Post(
|
||
makeSafeCallback<SmsService>(
|
||
this,
|
||
[&sms_pdu](SmsService* me) { me->HandleReceiveSMS(sms_pdu); }),
|
||
std::chrono::seconds(1));
|
||
} else { // Send SMS to remote host port
|
||
SendSmsToRemote(remote_host_port, sms_pdu);
|
||
}
|
||
} else if (sim_service_ && phone_number == sim_service_->GetPhoneNumber()) {
|
||
/* Local phone number */
|
||
thread_looper_->Post(
|
||
makeSafeCallback<SmsService>(
|
||
this, [sms_pdu](SmsService* me) { me->HandleReceiveSMS(sms_pdu); }),
|
||
std::chrono::seconds(1));
|
||
} /* else pretend send SMS success */
|
||
|
||
std::stringstream ss;
|
||
ss << "+CMGS: " << ++message_reference_;
|
||
responses.push_back(ss.str());
|
||
responses.push_back("OK");
|
||
client.SendCommandResponse(responses);
|
||
|
||
if (sms_pdu.IsNeededStatuReport()) {
|
||
int ref = message_reference_;
|
||
thread_looper_->Post(
|
||
makeSafeCallback<SmsService>(this,
|
||
[sms_pdu, ref](SmsService* me) {
|
||
me->HandleSMSStatuReport(sms_pdu, ref);
|
||
}),
|
||
std::chrono::seconds(1));
|
||
}
|
||
}
|
||
|
||
/* AT+CMGS callback function */
|
||
void SmsService::HandleReceiveSMS(PDUParser sms_pdu) {
|
||
std::string pdu = sms_pdu.CreatePDU();
|
||
if (pdu != "") {
|
||
SendUnsolicitedCommand("+CMT: 0");
|
||
SendUnsolicitedCommand(pdu);
|
||
}
|
||
}
|
||
|
||
/* Process AT+CMGW PDU */
|
||
void SmsService::HandleWriteSMSPduToSim(const Client& client, std::string& command) {
|
||
is_waiting_sms_to_sim_ = false;
|
||
|
||
SmsMessage message;
|
||
message.status = sms_status_on_sim_;
|
||
message.message = command;
|
||
int index = message_id_++;
|
||
messages_on_sim_card_[index] = message;
|
||
|
||
std::vector<std::string> responses;
|
||
std::stringstream ss;
|
||
ss << "+CMGW: " << index;
|
||
responses.push_back(ss.str());
|
||
responses.push_back("OK");
|
||
client.SendCommandResponse(responses);
|
||
}
|
||
|
||
/* SMS Status Report */
|
||
void SmsService::HandleSMSStatuReport(PDUParser sms_pdu, int message_reference) {
|
||
std::string response;
|
||
std::stringstream ss;
|
||
|
||
auto pdu = sms_pdu.CreateStatuReport(message_reference);
|
||
auto pdu_length = (pdu.size() - 2) / 2; // Not Including SMSC Address
|
||
if (pdu != "" && pdu_length > 0) {
|
||
ss << "+CDS: " << pdu_length;
|
||
SendUnsolicitedCommand(ss.str());
|
||
SendUnsolicitedCommand(pdu);
|
||
}
|
||
}
|
||
|
||
/* AT+REMOTESMS=PDU */
|
||
void SmsService::HandleReceiveRemoteSMS(const Client& /*client*/, std::string& command) {
|
||
CommandParser cmd(command);
|
||
cmd.SkipPrefix();
|
||
|
||
std::string pdu(*cmd);
|
||
PDUParser sms_pdu(pdu);
|
||
if (!sms_pdu.IsValidPDU()) {
|
||
LOG(ERROR) << "Failed to decode PDU";
|
||
return;
|
||
}
|
||
pdu = sms_pdu.CreatePDU();
|
||
if (pdu != "") {
|
||
SendUnsolicitedCommand("+CMT: 0");
|
||
SendUnsolicitedCommand(pdu);
|
||
}
|
||
}
|
||
} // namespace cuttlefish
|