device/google/cuttlefish/host/commands/modem_simulator/sim_service.cpp

1698 lines
53 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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/sim_service.h"
#include <android-base/logging.h>
#include <tinyxml2.h>
#include "common/libs/utils/files.h"
#include "host/commands/modem_simulator/device_config.h"
#include "host/commands/modem_simulator/network_service.h"
#include "host/commands/modem_simulator/pdu_parser.h"
#include "host/commands/modem_simulator/nvram_config.h"
namespace cuttlefish {
const std::pair<int, int> kSimPinSizeRange(4, 8);
constexpr int kSimPukSize = 8;
constexpr int kSimPinMaxRetryTimes = 3;
constexpr int kSimPukMaxRetryTimes = 10;
static const std::string kDefaultPinCode = "1234";
static const std::string kDefaultPukCode = "12345678";
static const std::string MF_SIM = "3F00";
static const std::string DF_TELECOM = "7F10";
static const std::string DF_PHONEBOOK = "5F3A";
static const std::string DF_GRAPHICS = "5F50";
static const std::string DF_GSM = "7F20";
static const std::string DF_CDMA = "7F25";
static const std::string DF_ADF = "7FFF"; // UICC access
// In an ADN record, everything but the alpha identifier
// is in a footer that's 14 bytes
constexpr int kFooterSizeBytes = 14;
// Maximum size of the un-extended number field
constexpr int kMaxNumberSizeBytes = 11;
constexpr int kMaxLogicalChannels = 3;
const std::map<SimService::SimStatus, std::string> gSimStatusResponse = {
{SimService::SIM_STATUS_ABSENT, ModemService::kCmeErrorSimNotInserted},
{SimService::SIM_STATUS_NOT_READY, ModemService::kCmeErrorSimBusy},
{SimService::SIM_STATUS_READY, "+CPIN: READY"},
{SimService::SIM_STATUS_PIN, "+CPIN: SIM PIN"},
{SimService::SIM_STATUS_PUK, "+CPIN: SIM PUK"},
};
/* SimFileSystem */
XMLElement* SimService::SimFileSystem::GetRootElement() {
return doc.RootElement();
}
std::string SimService::SimFileSystem::GetCommonIccEFPath(EFId efid) {
switch (efid) {
case EF_ADN:
case EF_FDN:
case EF_MSISDN:
case EF_SDN:
case EF_EXT1:
case EF_EXT2:
case EF_EXT3:
case EF_PSI:
return MF_SIM + DF_TELECOM;
case EF_ICCID:
case EF_PL:
return MF_SIM;
case EF_PBR:
// we only support global phonebook.
return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
case EF_IMG:
return MF_SIM + DF_TELECOM + DF_GRAPHICS;
default:
return {};
}
}
std::string SimService::SimFileSystem::GetUsimEFPath(EFId efid) {
switch(efid) {
case EF_SMS:
case EF_EXT5:
case EF_EXT6:
case EF_MWIS:
case EF_MBI:
case EF_SPN:
case EF_AD:
case EF_MBDN:
case EF_PNN:
case EF_OPL:
case EF_SPDI:
case EF_SST:
case EF_CFIS:
case EF_MAILBOX_CPHS:
case EF_VOICE_MAIL_INDICATOR_CPHS:
case EF_CFF_CPHS:
case EF_SPN_CPHS:
case EF_SPN_SHORT_CPHS:
case EF_FDN:
case EF_SDN:
case EF_EXT3:
case EF_MSISDN:
case EF_EXT2:
case EF_INFO_CPHS:
case EF_CSP_CPHS:
case EF_GID1:
case EF_GID2:
case EF_LI:
case EF_PLMN_W_ACT:
case EF_OPLMN_W_ACT:
case EF_HPLMN_W_ACT:
case EF_EHPLMN:
case EF_FPLMN:
case EF_LRPLMNSI:
case EF_HPPLMN:
return MF_SIM + DF_ADF;
case EF_PBR:
// we only support global phonebook.
return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
default:
std::string path = GetCommonIccEFPath(efid);
if (path.empty()) {
// The EFids in USIM phone book entries are decided by the card manufacturer.
// So if we don't match any of the cases above and if it's a USIM return
// the phone book path.
return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
}
return path;
}
}
XMLElement* SimService::SimFileSystem::FindAttribute(XMLElement *parent,
const std::string& attr_name,
const std::string& attr_value) {
if (parent == nullptr) {
return nullptr;
}
XMLElement* child = parent->FirstChildElement();
while (child) {
const XMLAttribute *attr = child->FindAttribute(attr_name.c_str());
if (attr && attr->Value() == attr_value) {
break;
}
child = child->NextSiblingElement();
}
return child;
};
XMLElement* SimService::SimFileSystem::AppendNewElement(XMLElement* parent,
const char* name) {
auto element = doc.NewElement(name);
parent->InsertEndChild(element);
return element;
}
XMLElement* SimService::SimFileSystem::AppendNewElementWithText(
XMLElement* parent, const char* name, const char* text) {
auto element = doc.NewElement(name);
auto xml_text = doc.NewText(text);
element->InsertEndChild(xml_text);
parent->InsertEndChild(element);
return element;
}
/* PinStatus */
bool SimService::PinStatus::CheckPasswordValid(std::string_view password) {
for (int i = 0; i < password.size(); i++) {
int c = (int)password[i];
if (c >= 48 && c <= 57) {
continue;
} else {
return false;
}
}
return true;
}
bool SimService::PinStatus::VerifyPIN(const std::string_view pin) {
if (pin.size() < kSimPinSizeRange.first || pin.size() > kSimPinSizeRange.second) {
return false;
}
if (!CheckPasswordValid(pin)) {
return false;
}
if (pin_remaining_times_ <= 0) {
return false;
}
std::string_view temp(pin_);
if (pin == temp) { // C++20 remove Operator!=
pin_remaining_times_ = kSimPinMaxRetryTimes;
return true;
}
pin_remaining_times_ -= 1;
return false;
}
bool SimService::PinStatus::VerifyPUK(const std::string_view puk) {
if (puk.size() != kSimPukSize) {
return false;
}
if (!CheckPasswordValid(puk)) {
return false;
}
if (puk_remaining_times_ <= 0) {
return false;
}
std::string_view temp(puk_);
if (puk == temp) { // C++20 remove Operator!=
pin_remaining_times_ = kSimPinMaxRetryTimes;
puk_remaining_times_ = kSimPukMaxRetryTimes;
return true;
}
puk_remaining_times_ -= 1;
return false;
}
bool SimService::PinStatus::ChangePIN(ChangeMode mode,
const std::string_view pin_or_puk,
const std::string_view new_pin) {
auto length = new_pin.length();
if (length < kSimPinSizeRange.first || length > kSimPinSizeRange.second) {
LOG(ERROR) << "Invalid digit number for PIN";
return false;
}
bool result = false;
if (mode == WITH_PIN) { // using old pin to change pin
result = VerifyPIN(pin_or_puk);
} else if (mode == WITH_PUK) { // using puk to change pin
result = VerifyPUK(pin_or_puk);
}
if (!result) {
LOG(ERROR) << "Incorrect PIN or PUK";
return false;
}
if (!CheckPasswordValid(new_pin)) {
return false;
}
std::string temp(new_pin);
pin_ = temp;
return true;
}
bool SimService::PinStatus::ChangePUK(const std::string_view puk,
const std::string_view new_puk) {
bool result = VerifyPUK(puk);
if (!result) {
LOG(ERROR) << "Incorrect PUK or no retry times";
return false;
}
if (new_puk.length() != kSimPukSize) {
LOG(ERROR) << "Invalid digit number for PUK";
return false;
}
std::string temp(new_puk);
puk_ = temp;
return true;
};
SimService::SimService(int32_t service_id, ChannelMonitor* channel_monitor,
ThreadLooper* thread_looper)
: ModemService(service_id, this->InitializeCommandHandlers(),
channel_monitor, thread_looper) {
InitializeServiceState();
}
std::vector<CommandHandler> SimService::InitializeCommandHandlers() {
std::vector<CommandHandler> command_handlers = {
CommandHandler(
"+CPIN?",
[this](const Client& client) { this->HandleSIMStatusReq(client); }),
CommandHandler("+CPIN=",
[this](const Client& client, std::string& cmd) {
this->HandleChangeOrEnterPIN(client, cmd);
}),
CommandHandler("+CRSM=",
[this](const Client& client, std::string& cmd) {
this->HandleSIM_IO(client, cmd);
}),
CommandHandler("+CSIM=",
[this](const Client& client, std::string& cmd) {
this->HandleCSIM_IO(client, cmd);
}),
CommandHandler(
"+CIMI",
[this](const Client& client) { this->HandleGetIMSI(client); }),
CommandHandler(
"+CICCID",
[this](const Client& client) { this->HandleGetIccId(client); }),
CommandHandler("+CLCK=",
[this](const Client& client, std::string& cmd) {
this->HandleFacilityLock(client, cmd);
}),
CommandHandler("+CCHO=",
[this](const Client& client, std::string& cmd) {
this->HandleOpenLogicalChannel(client, cmd);
}),
CommandHandler("+CCHC=",
[this](const Client& client, std::string& cmd) {
this->HandleCloseLogicalChannel(client, cmd);
}),
CommandHandler("+CGLA=",
[this](const Client& client, std::string& cmd) {
this->HandleTransmitLogicalChannel(client, cmd);
}),
CommandHandler("+CPWD=",
[this](const Client& client, std::string& cmd) {
this->HandleChangePassword(client, cmd);
}),
CommandHandler("+CPINR=",
[this](const Client& client, std::string& cmd) {
this->HandleQueryRemainTimes(client, cmd);
}),
CommandHandler("+CCSS",
[this](const Client& client, std::string& cmd) {
this->HandleCdmaSubscriptionSource(client, cmd);
}),
CommandHandler("+WRMP",
[this](const Client& client, std::string& cmd) {
this->HandleCdmaRoamingPreference(client, cmd);
}),
CommandHandler("^MBAU=",
[this](const Client& client, std::string& cmd) {
this->HandleSimAuthentication(client, cmd);
}),
};
return (command_handlers);
}
void SimService::InitializeServiceState() {
InitializeSimFileSystemAndSimState();
InitializeFacilityLock();
// Max logical channels: 3
logical_channels_ = {
LogicalChannel(1), LogicalChannel(2), LogicalChannel(kMaxLogicalChannels),
};
}
void SimService::InitializeSimFileSystemAndSimState() {
auto nvram_config = NvramConfig::Get();
auto sim_type = nvram_config->sim_type();
std::stringstream ss;
if (sim_type == 2) { // Special sim card for CtsCarrierApiTestCases
ss << "iccprofile_for_sim" << service_id_ << "_for_CtsCarrierApiTestCases.xml";
} else {
ss << "iccprofile_for_sim" << service_id_ << ".xml";
}
auto icc_profile_name = ss.str();
auto icc_profile_path = cuttlefish::modem::DeviceConfig::PerInstancePath(
icc_profile_name.c_str());
std::string file = icc_profile_path;
if (!cuttlefish::FileExists(icc_profile_path) ||
!cuttlefish::FileHasContent(icc_profile_path.c_str())) {
ss.clear();
ss.str("");
if (sim_type == 2) { // Special sim card for CtsCarrierApiTestCases
ss << "etc/modem_simulator/files/iccprofile_for_sim" << service_id_
<< "_for_CtsCarrierApiTestCases.xml";
} else {
ss << "etc/modem_simulator/files/iccprofile_for_sim" << service_id_ << ".xml";
}
auto etc_file_path =
cuttlefish::modem::DeviceConfig::DefaultHostArtifactsPath(ss.str());
if (!cuttlefish::FileExists(etc_file_path) || !cuttlefish::FileHasContent(etc_file_path)) {
sim_status_ = SIM_STATUS_ABSENT;
return;
}
file = etc_file_path;
}
sim_file_system_.file_path = icc_profile_path;
auto err = sim_file_system_.doc.LoadFile(file.c_str());
if (err != tinyxml2::XML_SUCCESS) {
LOG(ERROR) << "Unable to load XML file '" << file << " ', error " << err;
sim_status_ = SIM_STATUS_ABSENT;
return;
}
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
LOG(ERROR) << "Unable to find root element: IccProfile";
sim_status_ = SIM_STATUS_ABSENT;
return;
}
// Default value if iccprofile not configure pin state
sim_status_ = SIM_STATUS_READY;
pin1_status_.pin_ = kDefaultPinCode;
pin1_status_.puk_ = kDefaultPukCode;
pin1_status_.pin_remaining_times_ = kSimPinMaxRetryTimes;
pin1_status_.puk_remaining_times_ = kSimPukMaxRetryTimes;
pin2_status_.pin_ = kDefaultPinCode;
pin2_status_.puk_ = kDefaultPukCode;
pin2_status_.pin_remaining_times_ = kSimPinMaxRetryTimes;
pin2_status_.puk_remaining_times_ = kSimPukMaxRetryTimes;
XMLElement *pin_profile = root->FirstChildElement("PinProfile");
if (pin_profile) {
// Pin1 status
auto pin_state = pin_profile->FirstChildElement("PINSTATE");
if (pin_state) {
std::string state = pin_state->GetText();
if (state == "PINSTATE_ENABLED_NOT_VERIFIED") {
sim_status_ = SIM_STATUS_PIN;
} else if (state == "PINSTATE_ENABLED_BLOCKED") {
sim_status_ = SIM_STATUS_PUK;
}
}
auto pin_code = pin_profile->FirstChildElement("PINCODE");
if (pin_code) pin1_status_.pin_ = pin_code->GetText();
auto puk_code = pin_profile->FirstChildElement("PUKCODE");
if (puk_code) pin1_status_.puk_ = puk_code->GetText();
auto pin_remaining_times = pin_profile->FirstChildElement("PINREMAINTIMES");
if (pin_remaining_times) {
pin1_status_.pin_remaining_times_ = std::stoi(pin_remaining_times->GetText());
}
auto puk_remaining_times = pin_profile->FirstChildElement("PUKREMAINTIMES");
if (puk_remaining_times) {
pin1_status_.puk_remaining_times_ = std::stoi(puk_remaining_times->GetText());
}
// Pin2 status
auto pin2_code = pin_profile->FirstChildElement("PIN2CODE");
if (pin2_code) pin2_status_.pin_ = pin2_code->GetText();
auto puk2_code = pin_profile->FirstChildElement("PUK2CODE");
if (puk2_code) pin2_status_.puk_ = puk2_code->GetText();
auto pin2_remaining_times = pin_profile->FirstChildElement("PIN2REMAINTIMES");
if (pin2_remaining_times) {
pin2_status_.pin_remaining_times_ = std::stoi(pin2_remaining_times->GetText());
}
auto puk2_remaining_times = pin_profile->FirstChildElement("PUK2REMAINTIMES");
if (puk2_remaining_times) {
pin2_status_.puk_remaining_times_ = std::stoi(puk2_remaining_times->GetText());
}
}
}
void SimService::InitializeFacilityLock() {
/* Default disable */
facility_lock_ = {
{"SC", FacilityLock(FacilityLock::LockStatus::DISABLE)},
{"FD", FacilityLock(FacilityLock::LockStatus::DISABLE)},
{"AO", FacilityLock(FacilityLock::LockStatus::DISABLE)},
{"OI", FacilityLock(FacilityLock::LockStatus::DISABLE)},
{"OX", FacilityLock(FacilityLock::LockStatus::DISABLE)},
{"AI", FacilityLock(FacilityLock::LockStatus::DISABLE)},
{"IR", FacilityLock(FacilityLock::LockStatus::DISABLE)},
{"AB", FacilityLock(FacilityLock::LockStatus::DISABLE)},
{"AG", FacilityLock(FacilityLock::LockStatus::DISABLE)},
{"AC", FacilityLock(FacilityLock::LockStatus::DISABLE)},
};
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
LOG(ERROR) << "Unable to find root element: IccProfile";
sim_status_ = SIM_STATUS_ABSENT;
return;
}
XMLElement *facility_lock = root->FirstChildElement("FacilityLock");
if (!facility_lock) {
LOG(ERROR) << "Unable to find element: FacilityLock";
return;
}
for (auto iter = facility_lock_.begin(); iter != facility_lock_.end(); ++iter) {
auto lock_status = facility_lock->FirstChildElement(iter->first.c_str());
if (lock_status) {
std::string state = lock_status->GetText();
if (state == "ENABLE") {
iter->second.lock_status = FacilityLock::LockStatus::ENABLE;
}
}
}
}
void SimService::SavePinStateToIccProfile() {
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
LOG(ERROR) << "Unable to find root element: IccProfile";
sim_status_ = SIM_STATUS_ABSENT;
return;
}
XMLElement *pin_profile = root->FirstChildElement("PinProfile");
if (!pin_profile) {
pin_profile = sim_file_system_.AppendNewElement(root, "PinProfile");
}
const char* text = "PINSTATE_UNKNOWN";
if (sim_status_ == SIM_STATUS_PUK) {
text = "PINSTATE_ENABLED_BLOCKED";
} else {
auto iter = facility_lock_.find("SC");
if (iter != facility_lock_.end()) {
if (iter->second.lock_status == FacilityLock::ENABLE) {
text = "PINSTATE_ENABLED_NOT_VERIFIED";
}
}
}
// Pin1 status
auto pin_state = pin_profile->FirstChildElement("PINSTATE");
if (!pin_state) {
pin_state = sim_file_system_.AppendNewElementWithText(pin_profile, "PINSTATE", text);
} else {
pin_state->SetText(text);
}
auto pin_code = pin_profile->FirstChildElement("PINCODE");
if (!pin_code) {
pin_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PINCODE",
pin1_status_.pin_.c_str());
} else {
pin_code->SetText(pin1_status_.pin_.c_str());
}
auto puk_code = pin_profile->FirstChildElement("PUKCODE");
if (!puk_code) {
puk_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PUKCODE",
pin1_status_.puk_.c_str());
} else {
puk_code->SetText(pin1_status_.puk_.c_str());
}
std::stringstream ss;
ss << pin1_status_.pin_remaining_times_;
auto pin_remaining_times = pin_profile->FirstChildElement("PINREMAINTIMES");
if (!pin_remaining_times) {
pin_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
"PINREMAINTIMES", ss.str().c_str());
} else {
pin_remaining_times->SetText(ss.str().c_str());
}
ss.clear();
ss.str("");
ss << pin1_status_.puk_remaining_times_;
auto puk_remaining_times = pin_profile->FirstChildElement("PUKREMAINTIMES");
if (!puk_remaining_times) {
puk_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
"PUKREMAINTIMES", ss.str().c_str());
} else {
puk_remaining_times->SetText(ss.str().c_str());
}
// Pin2 status
auto pin2_code = pin_profile->FirstChildElement("PIN2CODE");
if (!pin2_code) {
pin2_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PIN2CODE",
pin2_status_.pin_.c_str());
} else {
pin2_code->SetText(pin2_status_.pin_.c_str());
}
auto puk2_code = pin_profile->FirstChildElement("PUK2CODE");
if (!puk2_code) {
puk2_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PUK2CODE",
pin2_status_.puk_.c_str());
} else {
puk2_code->SetText(pin2_status_.puk_.c_str());
}
ss.clear();
ss.str("");
ss << pin2_status_.pin_remaining_times_;
auto pin2_remaining_times = pin_profile->FirstChildElement("PIN2REMAINTIMES");
if (!pin2_remaining_times) {
pin2_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
"PINREMAINTIMES", ss.str().c_str());
} else {
pin2_remaining_times->SetText(ss.str().c_str());
}
ss.clear();
ss.str("");
ss << pin2_status_.puk_remaining_times_;
auto puk2_remaining_times = pin_profile->FirstChildElement("PUK2REMAINTIMES");
if (!puk2_remaining_times) {
puk2_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
"PUK2REMAINTIMES", ss.str().c_str());
} else {
puk2_remaining_times->SetText(ss.str().c_str());
}
// Save file
sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
}
void SimService::SaveFacilityLockToIccProfile() {
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
LOG(ERROR) << "Unable to find root element: IccProfile";
sim_status_ = SIM_STATUS_ABSENT;
return;
}
XMLElement *facility_lock = root->FirstChildElement("FacilityLock");
if (!facility_lock) {
facility_lock = sim_file_system_.AppendNewElement(root, "FacilityLock");
}
const char* text = "DISABLE";
for (auto iter = facility_lock_.begin(); iter != facility_lock_.end(); ++iter) {
if (iter->second.lock_status == FacilityLock::LockStatus::ENABLE) {
text = "ENABLE";
} else {
text = "DISABLE";
}
auto element = facility_lock->FirstChildElement(iter->first.c_str());
if (!element) {
element = sim_file_system_.AppendNewElementWithText(facility_lock,
iter->first.c_str(), text);
} else {
element->SetText(text);
}
}
sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
InitializeSimFileSystemAndSimState();
InitializeFacilityLock();
}
bool SimService::IsFDNEnabled() {
auto iter = facility_lock_.find("FD");
if (iter != facility_lock_.end() &&
iter->second.lock_status == FacilityLock::LockStatus::ENABLE) {
return true;
}
return false;
}
bool SimService::IsFixedDialNumber(std::string_view number) {
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) return false;
auto path = SimFileSystem::GetUsimEFPath(SimFileSystem::EFId::EF_FDN);
size_t pos = 0;
auto parent = root;
while (pos < path.length()) {
std::string sub_path(path.substr(pos, 4));
auto app = SimFileSystem::FindAttribute(parent, "path", sub_path);
if (!app) return false;
pos += 4;
parent = app;
}
XMLElement* ef = SimFileSystem::FindAttribute(parent, "id", "6F3B");
if (!ef) return false;
XMLElement *final = ef->FirstChildElement("SIMIO");
while (final) {
std::string record = final->GetText();
int footerOffset = record.length() - kFooterSizeBytes * 2;
int numberLength = (record[footerOffset] - '0') * 16 +
record[footerOffset + 1] - '0';
if (numberLength > kMaxNumberSizeBytes) { // Invalid number length
final = final->NextSiblingElement("SIMIO");
continue;
}
std::string bcd_fdn = "";
if (numberLength * 2 == 16) { // Skip Type(91) and Country Code(68)
bcd_fdn = record.substr(footerOffset + 6, numberLength * 2 - 4);
} else { // Skip Type(81)
bcd_fdn = record.substr(footerOffset + 4, numberLength * 2 - 2);
}
std::string fdn = PDUParser::BCDToString(bcd_fdn);
if (fdn == number) {
return true;
}
final = final->NextSiblingElement("SIMIO");
}
return false;
}
XMLElement* SimService::GetIccProfile() {
return sim_file_system_.GetRootElement();
}
std::string SimService::GetPhoneNumber() {
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) return "";
auto path = SimFileSystem::GetUsimEFPath(SimFileSystem::EFId::EF_MSISDN);
size_t pos = 0;
auto parent = root;
while (pos < path.length()) {
std::string sub_path(path.substr(pos, 4));
auto app = SimFileSystem::FindAttribute(parent, "path", sub_path);
if (!app) return "";
pos += 4;
parent = app;
}
XMLElement* ef = SimFileSystem::FindAttribute(parent, "id", "6F40");
if (!ef) return "";
XMLElement *final = SimFileSystem::FindAttribute(ef, "cmd", "B2");;
if (!final) return "";
std::string record = final->GetText();
int footerOffset = record.length() - kFooterSizeBytes * 2;
int numberLength = (record[footerOffset] - '0') * 16 +
record[footerOffset + 1] - '0';
if (numberLength > kMaxNumberSizeBytes) { // Invalid number length
return "";
}
std::string bcd_number = "";
if (numberLength * 2 == 16) { // Skip Type(91) and Country Code(68)
bcd_number = record.substr(footerOffset + 6, numberLength * 2 - 4);
} else { // Skip Type(81)
bcd_number = record.substr(footerOffset + 4, numberLength * 2 - 2);
}
return PDUParser::BCDToString(bcd_number);
}
SimService::SimStatus SimService::GetSimStatus() const {
return sim_status_;
}
std::string SimService::GetSimOperator() {
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) return "";
XMLElement* mf = SimFileSystem::FindAttribute(root, "path", MF_SIM);
if (!mf) return "";
XMLElement* df = SimFileSystem::FindAttribute(mf, "path", DF_ADF);
if (!df) return "";
XMLElement* ef = SimFileSystem::FindAttribute(df, "id", "6F07");
if (!ef) return "";
XMLElement *cimi = ef->FirstChildElement("CIMI");
if (!cimi) return "";
std::string imsi = cimi->GetText();
ef = SimFileSystem::FindAttribute(df, "id", "6FAD");
if (!ef) return "";
XMLElement *sim_io = ef->FirstChildElement("SIMIO");
while (sim_io) {
const XMLAttribute *attr_cmd = sim_io->FindAttribute("cmd");
std::string attr_value = attr_cmd ? attr_cmd->Value() : "";
if (attr_cmd && attr_value == "B0") {
break;
}
sim_io = sim_io->NextSiblingElement("SIMIO");
}
if (!sim_io) return "";
std::string length = sim_io->GetText();
int mnc_size = std::stoi(length.substr(length.size() -2));
return imsi.substr(0, 3 + mnc_size);
}
void SimService::SetupDependency(NetworkService* net) {
network_service_ = net;
}
/**
* AT+CPIN
* Set command sends to the MT a password which is necessary before it can be
* operated.
* Read command returns an alphanumeric string indicating whether some
* password is required or not.
*
* Command Possible response(s)
* +CPIN=<pin>[,<newpin>] +CME ERROR: <err>
* +CPIN? +CPIN: <code>
* +CME ERROR: <err>
* <pin>, <newpin>: string type values.
* <code> values reserved by the present document:
* READY MT is not pending for any password
* SIM PIN MT is waiting SIM PIN to be given
* SIM PUK MT is waiting SIM PUK to be given
*
* see RIL_REQUEST_GET_SIM_STATUS in RIL
*/
void SimService::HandleSIMStatusReq(const Client& client) {
std::vector<std::string> responses;
auto iter = gSimStatusResponse.find(sim_status_);
if (iter != gSimStatusResponse.end()) {
responses.push_back(iter->second);
responses.push_back("OK");
} else {
sim_status_ = SIM_STATUS_ABSENT;
responses.push_back(kCmeErrorSimNotInserted);
}
client.SendCommandResponse(responses);
}
/**
* AT+CRSM
* By using this command instead of Generic SIM Access +CSIM TE application
* has easier but more limited access to the SIM database.
*
* Command Possible response(s)
* +CRSM=<command>[,<fileid> +CRSM: <sw1>,<sw2>[,<response>]
* [,<P1>,<P2>,<P3>[,<data>[,<pathid>]]]] +CME ERROR: <err>
*
* <command>: (command passed on by the MT to the SIM; refer 3GPP TS 51.011 [28]):
* 176 READ BINARY
* 178 READ RECORD
* 192 GET RESPONSE
* 214 UPDATE BINARY
* 220 UPDATE RECORD
* 242 STATUS
* 203 RETRIEVE DATA
* 219 SET DATA
*
* <fileid>: integer type; this is the identifier of a elementary datafile on SIM.
* Mandatory for every command except STATUS.
*
* <P1>, <P2>, <P3>: integer type; parameters passed on by the MT to the SIM.
* These parameters are mandatory for every command,
* except GET RESPONSE and STATUS.
*
* <data>: information which shall be written to the SIM (hexadecimal character format).
*
* <pathid>: string type; contains the path of an elementary file on the SIM/UICC
* in hexadecimal format.
*
* <sw1>, <sw2>: integer type; information from the SIM about the execution of
* the actual command.
*
* <response>: response of a successful completion of the command previously issued
* (hexadecimal character format; refer +CSCS).
*/
void SimService::HandleSIM_IO(const Client& client,
const std::string& command) {
std::vector<std::string> kFileNotFoud = {"+CRSM: 106,130", "OK"};
std::vector<std::string> responses;
CommandParser cmd(command);
cmd.SkipPrefix(); // skip "AT+CRSM="
if (*cmd == "242,0,0,0,0") { // for cts teset
responses.push_back("+CRSM: 144,0,62338202782183023F00A50C80016187010183040007DBF08A01058B062F0601020002C60C90016083010183010A83010D8102FFFF");
responses.push_back("OK");
client.SendCommandResponse(responses);
return;
}
auto c = cmd.GetNextStrDeciToHex();
auto id = cmd.GetNextStrDeciToHex();
auto p1 = cmd.GetNextStrDeciToHex();
auto p2 = cmd.GetNextStrDeciToHex();
auto p3 = cmd.GetNextStrDeciToHex();
auto data = cmd.GetNextStr(',');
std::string path(cmd.GetNextStr());
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
LOG(ERROR) << "Unable to find root element: IccProfile";
client.SendCommandResponse(kCmeErrorOperationNotAllowed);
return;
}
SimFileSystem::EFId fileid = (SimFileSystem::EFId)std::stoi(id, nullptr, 16);
if (path == "") {
path = SimFileSystem::GetUsimEFPath(fileid);
}
// EF_ADN under DF_PHONEBOOK is mapped to EF_ADN under DF_TELECOM per
// 3GPP TS 31.102 4.4.2
if (fileid == SimFileSystem::EF_ADN &&
path == SimFileSystem::GetUsimEFPath(fileid)) {
id = "4F3A";
path = MF_SIM + DF_TELECOM + DF_PHONEBOOK;
}
size_t pos = 0;
auto parent = root;
while (pos < path.length()) {
std::string sub_path(path.substr(pos, 4));
auto app = SimFileSystem::FindAttribute(parent, "path", sub_path);
if (!app) {
client.SendCommandResponse(kFileNotFoud);
return;
}
pos += 4;
parent = app;
}
XMLElement* ef = SimFileSystem::FindAttribute(parent, "id", id);
if (!ef) {
client.SendCommandResponse(kFileNotFoud);
return;
}
XMLElement *final = ef->FirstChildElement("SIMIO");
while (final) {
const XMLAttribute *attr_cmd = final->FindAttribute("cmd");
const XMLAttribute *attr_p1 = final->FindAttribute("p1");
const XMLAttribute *attr_p2 = final->FindAttribute("p2");
const XMLAttribute *attr_p3 = final->FindAttribute("p3");
const XMLAttribute *attr_data = final->FindAttribute("data");
if (c != "DC" && c != "D6") { // Except UPDATE RECORD or UPDATE BINARY
if ((attr_cmd && attr_cmd->Value() != c) ||
(attr_data && attr_data->Value() != data)) {
final = final->NextSiblingElement("SIMIO");
continue;
}
}
if (attr_p1 && attr_p1->Value() == p1 &&
attr_p2 && attr_p2->Value() == p2 &&
attr_p3 && attr_p3->Value() == p3) {
break;
}
final = final->NextSiblingElement("SIMIO");
}
if (!final) {
client.SendCommandResponse(kFileNotFoud);
return;
}
std::string response = "+CRSM: ";
if (c == "DC" || c == "D6") {
std::string temp = "144,0,";
temp += data;
final->SetText(temp.c_str());
sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
response.append("144,0");
} else {
response.append(final->GetText());
}
responses.push_back(response);
responses.push_back("OK");
client.SendCommandResponse(responses);
}
void SimService::OnSimStatusChanged() {
auto ptr = network_service_;
if (ptr) {
ptr->OnSimStatusChanged(sim_status_);
}
}
bool SimService::checkPin1AndAdjustSimStatus(std::string_view pin) {
if (pin1_status_.VerifyPIN(pin) == true) {
sim_status_ = SIM_STATUS_READY;
OnSimStatusChanged();
return true;
}
if (pin1_status_.pin_remaining_times_ <= 0) {
sim_status_ = SIM_STATUS_PUK;
OnSimStatusChanged();
}
return false;
}
/* AT+CSIM */
void SimService::HandleCSIM_IO(const Client& client,
const std::string& command) {
std::vector<std::string> responses;
CommandParser cmd(command);
cmd.SkipPrefix(); // skip "AT+CSIM="
cmd.SkipComma();
auto data = cmd.GetNextStr();
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
LOG(ERROR) << "Unable to find root element: IccProfile";
client.SendCommandResponse(kCmeErrorOperationNotAllowed);
return;
}
// Get aid
XMLElement* df = SimFileSystem::FindAttribute(root, "aid", "CSIM");
if (!df) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
std::string data_value(data);
if (data_value.length() > 10) { // for open channel with csim
responses.push_back("+CSIM: 4,9000");
responses.push_back("OK");
client.SendCommandResponse(responses);
return;
}
XMLElement* final = SimFileSystem::FindAttribute(df, "cmd", data_value);
if (!final) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
auto id = data_value.substr(data_value.length() - 2, 2);
std::vector<LogicalChannel>::iterator iter = logical_channels_.begin();
for (; iter != logical_channels_.end(); ++iter) {
if (!iter->is_open) break;
}
if (iter != logical_channels_.end() && iter->session_id ==stoi(id)) {
iter->is_open = true;
iter->df_name = "CSIM";
}
std::stringstream ss;
ss << "+CSIM: " << final->GetText();
responses.push_back(ss.str());
responses.push_back("OK");
client.SendCommandResponse(responses);
}
bool SimService::ChangePin1AndAdjustSimStatus(PinStatus::ChangeMode mode,
std::string_view pin,
std::string_view new_pin) {
if (pin1_status_.ChangePIN(mode, pin, new_pin) == true) {
sim_status_ = SIM_STATUS_READY;
OnSimStatusChanged();
return true;
}
if (sim_status_ == SIM_STATUS_READY && pin1_status_.pin_remaining_times_ <= 0) {
sim_status_ = SIM_STATUS_PIN;
OnSimStatusChanged();
} else if (sim_status_ == SIM_STATUS_PIN && pin1_status_.puk_remaining_times_ <= 0) {
sim_status_ = SIM_STATUS_ABSENT;
OnSimStatusChanged();
}
return false;
}
void SimService::HandleChangeOrEnterPIN(const Client& client,
const std::string& command) {
std::vector<std::string> responses;
CommandParser cmd(command);
cmd.SkipPrefix(); // skip "AT+CPIN="
switch (sim_status_) {
case SIM_STATUS_ABSENT:
responses.push_back(kCmeErrorSimNotInserted);
break;
case SIM_STATUS_NOT_READY:
responses.push_back(kCmeErrorSimBusy);
break;
case SIM_STATUS_READY: {
/*
* this may be a request to change the PIN with pin and new pin:
* AT+CPIN=pin,newpin
* or a request to enter the PIN2
* AT+CPIN=pin2
*/
auto pos = cmd->find(',');
if (pos != std::string_view::npos) { // change pin with new pin
auto pin = cmd.GetNextStr(',');
auto new_pin = *cmd;
if (ChangePin1AndAdjustSimStatus(PinStatus::WITH_PIN, pin, new_pin)) {
responses.push_back("OK");
} else {
responses.push_back(kCmeErrorIncorrectPassword); /* incorrect PIN */
}
} else { // verify pin2
if (pin2_status_.VerifyPIN(*cmd) == true) {
responses.push_back("OK");
} else {
responses.push_back(kCmeErrorIncorrectPassword); /* incorrect PIN2 */
}
}
break;
}
case SIM_STATUS_PIN: { /* waiting for PIN */
if (checkPin1AndAdjustSimStatus(*cmd) == true) {
responses.push_back("OK");
} else {
responses.push_back(kCmeErrorIncorrectPassword);
}
break;
}
case SIM_STATUS_PUK: {
/*
* this may be a request to unlock the puk with new pin:
* AT+CPIN=puk,newpin
*/
auto pos = cmd->find(',');
if (pos != std::string_view::npos) {
auto puk = cmd.GetNextStr(',');
auto new_pin = *cmd;
if (ChangePin1AndAdjustSimStatus(PinStatus::WITH_PUK, puk, new_pin)) {
responses.push_back("OK");
} else {
responses.push_back(kCmeErrorIncorrectPassword);
}
} else {
responses.push_back(kCmeErrorOperationNotAllowed);
}
break;
}
default:
responses.push_back(kCmeErrorOperationNotAllowed);
break;
}
client.SendCommandResponse(responses);
}
/**
* AT+CIMI
* Execution command causes the TA to return <IMSI>, which is intended to
* permit the TE to identify the individual SIM card or active application in
* the UICC (GSM or USIM) which is attached to MT.
*
* Command Possible response(s)
* +CIMI <IMSI>
* +CME ERROR: <err>
*
* <IMSI>: International Mobile Subscriber Identity (string without double quotes)
*
* see RIL_REQUEST_GET_IMSI in RIL
*/
void SimService::HandleGetIMSI(const Client& client) {
std::vector<std::string> responses;
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
client.SendCommandResponse(kCmeErrorOperationNotAllowed);
return;
}
XMLElement* mf = SimFileSystem::FindAttribute(root, "path", MF_SIM);
if (!mf) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
XMLElement* df = SimFileSystem::FindAttribute(mf, "path", DF_ADF);
if (!df) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
XMLElement* ef = SimFileSystem::FindAttribute(df, "id", "6F07");
if (!ef) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
XMLElement *final = ef->FirstChildElement("CIMI");
if (!final) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
responses.push_back(final->GetText());
responses.push_back("OK");
client.SendCommandResponse(responses);
}
/**
* AT+CICCID
* Integrated Circuit Card IDentifier (ICCID) is Unique Identifier of the SIM CARD.
* File is located in the SIM card at EFiccid (0x2FE2).
*
* see RIL_REQUEST_GET_SIM_STATUS in RIL
*/
void SimService::HandleGetIccId(const Client& client) {
std::vector<std::string> responses;
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
client.SendCommandResponse(kCmeErrorOperationNotAllowed);
return;
}
XMLElement* mf = SimFileSystem::FindAttribute(root, "path", MF_SIM);
if (!mf) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
XMLElement* ef = SimFileSystem::FindAttribute(mf, "id", "2FE2");
if (!ef) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
XMLElement *final = ef->FirstChildElement("CCID");
if (!final) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
responses.push_back(final->GetText());
responses.push_back("OK");
client.SendCommandResponse(responses);
}
/*
* AT+CLCK
* Execute command is used to lock, unlock or interrogate a MT or a network
* facility <fac>.
*
* Command Possible response(s)
* +CLCK=<fac>, <mode> [, <password> OK or +CME ERROR: <err>
* [, <class>]] +CLCK: <status>[,<class1>[<CR><LF>+CLCK:
* <status>,<class2>[...]](when mode=2,its
* in inquiry status.)
* <fac> values reserved by the present document:
* "SC": SIM (lock SIM/UICC card installed in the currently selected card
* slot) (SIM/UICC asks password in MT powerup and when this lock
* command issued).
* "FD": SIM card or active application in the UICC (GSM or USIM) fixed
* dialling memory feature (if PIN2 authentication has not been done
* during the current session, PIN2 is required as <passwd>).
* <mode>: integer type
* 0: unlock
* 1: lock
* 2: query status
* <status>: integer type
* 0: not active
* 1: active
* <passwd>: string type; shall be the same as password specified for the
* facility from the MT user interface or with command
* Change Password +CPWD.
* <classx> is a sum of integers each representing a class of information
* (default 7 - voice, data and fax):
* 1 voice (telephony)
* 2 data
* 4 fax (facsimile services)
* 8 short message service
* 16 data circuit sync
* 32 data circuit async
* 64 dedicated packet access
* 128 dedicated PAD access
*
* see RIL_REQUEST_SET_FACILITY_LOCK in RIL
*/
void SimService::HandleFacilityLock(const Client& client,
const std::string& command) {
CommandParser cmd(command);
std::string lock(cmd.GetNextStr());
int mode = cmd.GetNextInt();
auto password = cmd.GetNextStr();
// Ignore class from RIL
auto iter = facility_lock_.find(lock);
if (iter == facility_lock_.end()) {
client.SendCommandResponse(kCmeErrorOperationNotSupported);
return;
}
std::stringstream ss;
std::vector<std::string> responses;
switch (mode) {
case FacilityLock::Mode::QUERY: {
ss << "+CLCK: " << iter->second.lock_status;
responses.push_back(ss.str());
responses.push_back("OK");
break;
}
case FacilityLock::Mode::LOCK:
case FacilityLock::Mode::UNLOCK: {
if (lock == "SC") {
if (checkPin1AndAdjustSimStatus(password) == true) {
iter->second.lock_status = (FacilityLock::LockStatus)mode;
responses.push_back("OK");
} else {
responses.push_back(kCmeErrorIncorrectPassword);
}
} else if (lock == "FD") {
if (pin2_status_.VerifyPIN(password) == true) {
iter->second.lock_status = (FacilityLock::LockStatus)mode;
responses.push_back("OK");
} else {
responses.push_back(kCmeErrorIncorrectPassword);
}
} else { // Don't need password except 'SC' and 'FD'
iter->second.lock_status = (FacilityLock::LockStatus)mode;
responses.push_back("OK");
}
break;
}
default:
responses.push_back(kCmeErrorInCorrectParameters);
break;
}
client.SendCommandResponse(responses);
}
/**
* AT+CCHO
* The currently selected UICC will open a new logical channel; select the
* application identified by the <dfname> received with this command and return
* a session Id as the response.
*
* Command Possible response(s)
* +CCHO=<dfname> <sessionid>
* +CME ERROR: <err>
*
* <dfname>: all selectable applications in the UICC are referenced by a DF
* name coded on 1 to 16 bytes.
* <sessionid>: integer type; a session Id to be used in order to target a
* specific application on the smart card (e.g. (U)SIM, WIM, ISIM)
* using logical channels mechanism.
*
* see RIL_REQUEST_SIM_OPEN_CHANNEL in RIL
*/
void SimService::HandleOpenLogicalChannel(const Client& client,
const std::string& command) {
std::vector<std::string> responses;
CommandParser cmd(command);
cmd.SkipPrefix(); // skip AT+CCHO=
if (cmd->empty()) {
client.SendCommandResponse(kCmeErrorInCorrectParameters);
return;
}
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
client.SendCommandResponse(kCmeErrorOperationNotAllowed);
return;
}
std::string aid_value(*cmd);
XMLElement* df = SimFileSystem::FindAttribute(root, "aid", aid_value);
if (!df) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
std::vector<LogicalChannel>::iterator iter = logical_channels_.begin();
for (; iter != logical_channels_.end(); ++iter) {
if (!iter->is_open) break;
}
if (iter != logical_channels_.end()) {
iter->is_open = true;
iter->df_name = *cmd;
std::stringstream ss;
ss << iter->session_id;
responses.push_back(ss.str());
responses.push_back("OK");
} else {
responses.push_back(kCmeErrorMemoryFull);
}
client.SendCommandResponse(responses);
}
/**
* AT+CCHC
* This command asks the ME to close a communication session with the active
* UICC.
*
* Command Possible response(s)
* +CCHC=<sessionid> +CCHC
* +CME ERROR: <err>
* <sessionid>: see AT+CCHO
*
* see RIL_REQUEST_SIM_CLOSE_CHANNEL in RIL
*/
void SimService::HandleCloseLogicalChannel(const Client& client,
const std::string& command) {
std::vector<std::string> responses;
CommandParser cmd(command);
cmd.SkipPrefix(); // skip AT+CCHC=
int session_id = cmd.GetNextInt();
std::vector<LogicalChannel>::iterator iter = logical_channels_.begin();
for (; iter != logical_channels_.end(); ++iter) {
if (iter->session_id == session_id) break;
}
if (iter != logical_channels_.end() && iter->is_open) {
iter->is_open = false;
iter->df_name.clear();
responses.push_back("+CCHC");
responses.push_back("OK");
} else {
responses.push_back(kCmeErrorNotFound);
}
client.SendCommandResponse(responses);
}
/**
* AT+CGLA
* Set command transmits to the MT the <command> it then shall send as it is
* to the selected UICC. In the same manner the UICC <response> shall be sent
* back by the MT to the TA as it is.
*
* Command Possible response(s)
* +CGLA=<sessionid>,<length>, +CGLA: <length>,<response>
* +CME ERROR: <err>
* <sessionid>: AT+CCHO
* <length>: integer type; length of the characters that are sent to TE in
* <command> or <response> .
* <command>: command passed on by the MT to the UICC in the format as described
* in 3GPP TS 31.101 [65] (hexadecimal character format; refer +CSCS).
* <response>: response to the command passed on by the UICC to the MT in the
* format as described in 3GPP TS 31.101 [65] (hexadecimal character
* format; refer +CSCS).
*
* see RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL in RIL
*/
void SimService::HandleTransmitLogicalChannel(const Client& client,
const std::string& command) {
std::vector<std::string> responses;
CommandParser cmd(command);
cmd.SkipPrefix(); // skip AT+CGLA=
int session_id = cmd.GetNextInt();
int length = cmd.GetNextInt();
if (cmd->length() != length) {
client.SendCommandResponse(kCmeErrorInCorrectParameters);
return;
}
// Check if session id is opened
auto iter = logical_channels_.begin();
for (; iter != logical_channels_.end(); ++iter) {
if (iter->session_id == session_id && iter->is_open) {
break;
}
}
if (iter == logical_channels_.end()) {
client.SendCommandResponse(kCmeErrorInvalidIndex);
return;
}
XMLElement *root = sim_file_system_.GetRootElement();
if (!root) {
client.SendCommandResponse(kCmeErrorOperationNotAllowed);
return;
}
// Get aid
XMLElement* df = SimFileSystem::FindAttribute(root, "aid", iter->df_name);
if (!df) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
if (iter->df_name != "CSIM") {
std::string command_vaule(*cmd);
if (command_vaule.substr(2, 2) == "a4") {
last_file_id_ = command_vaule.substr(command_vaule.length() - 4, 4);
}
df = SimFileSystem::FindAttribute(df, "id", last_file_id_);
if (!df) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
}
std::string attr_value(*cmd);
XMLElement* final = SimFileSystem::FindAttribute(df, "cmd", attr_value);
if (!final) {
client.SendCommandResponse(kCmeErrorNotFound);
return;
}
std::stringstream ss;
ss << "+CGLA: " << final->GetText();
responses.push_back(ss.str());
responses.push_back("OK");
client.SendCommandResponse(responses);
}
/**
* AT+CPWD
* Action command sets a new password for the facility lock function defined
* by command Facility Lock +CLCK
*
* Command Possible response(s)
* +CPWD=<fac>,<oldpwd>,<newpwd> +CME ERROR: <err>
*
* <fac>:
* "P2" SIM PIN2
* refer Facility Lock +CLCK for other values
* <oldpwd>, <newpwd>:
* string type; <oldpwd> shall be the same as password specified for the
* facility from the MT user interface or with command Change Password +CPWD
* and <newpwd> is the new password; maximum length of password can be determined
* with <pwdlength>
* <pwdlength>: integer type maximum length of the password for the facility
*/
void SimService::HandleChangePassword(const Client& client,
const std::string& command) {
std::string response = kCmeErrorIncorrectPassword;
CommandParser cmd(command);
cmd.SkipPrefix();
auto lock = cmd.GetNextStr();
auto old_password = cmd.GetNextStr();
auto new_password = cmd.GetNextStr();
if (lock == "SC") {
if (ChangePin1AndAdjustSimStatus(PinStatus::WITH_PIN, old_password, new_password)) {
response = "OK";
}
} else if (lock == "P2" || lock == "FD") {
if (pin2_status_.ChangePIN(PinStatus::WITH_PIN, old_password, new_password)) {
response = "OK";
}
} else {
response = kCmeErrorOperationNotSupported;;
}
client.SendCommandResponse(response);
}
/**
* AT+CPINR
* Execution command cause the MT to return the number of remaining PIN retries
* for the MT passwords with intermediate result code
*
* Command Possible response(s)
* +CPINR[=<sel_code>] +CPINR: <code>,<retries>[,<default_retries>]
*
* <retries>:
* integer type. Number of remaining retries per PIN.
* <default_retries>:
* integer type. Number of default/initial retries per PIN.
* <code>:
* Type of PIN. All values listed under the description of the AT+CPIN command
* <sel_code>: String type. Same values as for the <code> and <ext_code> parameters.
* these values are strings and shall be indicated within double quotes.
*/
void SimService::HandleQueryRemainTimes(const Client& client,
const std::string& command) {
std::vector<std::string> responses;
std::stringstream ss;
CommandParser cmd(command);
cmd.SkipPrefix();
auto lock_type = cmd.GetNextStr();
if (lock_type == "SIM PIN") {
ss << "+CPINR: SIM PIN," << pin1_status_.pin_remaining_times_ << ","
<< kSimPinMaxRetryTimes;
} else if (lock_type == "SIM PUK") {
ss << "+CPINR: SIM PUK," << pin1_status_.puk_remaining_times_ << ","
<< kSimPukMaxRetryTimes;
} else if (lock_type == "SIM PIN2") {
ss << "+CPINR: SIM PIN2," << pin2_status_.pin_remaining_times_ << ","
<< kSimPinMaxRetryTimes;
} else if (lock_type == "SIM PUK2") {
ss << "+CPINR: SIM PUK2," << pin2_status_.puk_remaining_times_ << ","
<< kSimPukMaxRetryTimes;
} else {
responses.push_back(kCmeErrorInCorrectParameters);
client.SendCommandResponse(responses);
return;
}
responses.push_back(ss.str());
responses.push_back("OK");
client.SendCommandResponse(responses);
}
/**
* see
* RIL_REQUEST_CDMA_SET_SUBSCRIPTION or
* RIL_REQUEST_CDMA_GET_SUBSCRIPTION in RIL
*/
void SimService::HandleCdmaSubscriptionSource(const Client& client,
const std::string& command) {
std::vector<std::string> responses;
CommandParser cmd(command);
if (*cmd == "AT+CCSS?") { // Query
std::stringstream ss;
ss << "+CCSS: " << cdma_subscription_source_;
responses.push_back(ss.str());
} else { // Set
cdma_subscription_source_ = cmd.GetNextInt();
}
responses.push_back("OK");
client.SendCommandResponse(responses);
}
/**
* see
* RIL_REQUEST_CDMA_SET_ROAMNING_PREFERENCE or
* RIL_REQUEST_CDMA_GET_ROAMNING_PREFERENCE in RIL
*/
void SimService::HandleCdmaRoamingPreference(const Client& client,
const std::string& command) {
std::vector<std::string> responses;
CommandParser cmd(command);
if (*cmd == "AT+WRMP?") { // Query
std::stringstream ss;
ss << "+WRMP: " << cdma_roaming_preference_;
responses.push_back(ss.str());
} else { // Set
cdma_roaming_preference_ = cmd.GetNextInt();
}
responses.push_back("OK");
client.SendCommandResponse(responses);
}
void SimService::HandleSimAuthentication(const Client& client,
const std::string& command) {
std::vector<std::string> responses;
CommandParser cmd(command);
cmd.SkipPrefix();
// Input format: ^MBAU=<RAND>[,<AUTN>]
auto cmds = cmd.GetNextStr();
// Output format: ^MBAU: <STATUS>[,<KC>,<SRES>][,<CK>,<IK>,<RES/AUTS>]
std::stringstream ss;
// Authentication challenges done in CTS.
if (cmds == "2713AB0BA8E8E7D8F1D74545BA03F563") {
// CarrierApiTest#testGetIccAuthentication (base64Challenge)
ss << "^MBAU: 0,8F2980FC3872FF89,E9620240";
} else if (cmds == "C3718EC16B3C2A66F8A7200A64069F04") {
// CarrierApiTest#testGetIccAuthentication (base64Challenge2)
ss << "^MBAU: 0,CFDA6C980502DA48,F7E53577";
} else if (cmds == "11111111111111111111111111111111") {
// CarrierApiTest#testEapSimAuthentication
ss << "^MBAU: 0,0000000000000000,00000000";
} else if (cmds == "11111111111111111111111111111111,12351417161900001130131215141716") {
// CarrierApiTest#testEapAkaAuthentication
// Note: the "DB" prefix gets appended where the RIL parses this response.
ss << "^MBAU: 0,111013121514171619181B1A1D1C1F1E,1013121514171619181B1A1D1C1F1E11,"
"13121514171619181B1A1D1C1F1E1110";
}
responses.push_back(ss.str());
responses.push_back("OK");
client.SendCommandResponse(responses);
}
} // namespace cuttlefish