/*
* Copyright ( C ) 2025 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 .
*/
#define FAILURE_DEBUG_PREFIX "RadioSim"
#include <charconv>
#include <format>
#include <tuple>
#include <vector>
#include "RadioSim.h"
#include "atCmds.h"
#include "debug.h"
#include "hexbin.h"
#include "makeRadioResponseInfo.h"
namespace aidl {
namespace android {
namespace hardware {
namespace radio {
namespace implementation {
namespace {
using namespace std::literals;
enum class AuthContext {
SIM = 128 ,
AKA = 129 ,
};
enum class StkCmdType {
RUN_AT = 0 x34,
SEND_DTMF = 0 x14,
SEND_SMS = 0 x13,
SEND_SS = 0 x11,
SEND_USSD = 0 x12,
PLAY_TONE = 0 x20,
OPEN_CHANNEL = 0 x40,
CLOSE_CHANNEL = 0 x41,
RECEIVE_DATA = 0 x42,
SEND_DATA = 0 x43,
GET_CHANNEL_STATUS = 0 x44,
REFRESH = 0 x01,
};
#define USIM_DATA_OFFSET_2 2
#define USIM_DATA_OFFSET_3 3
#define USIM_RESPONSE_DATA_FILE_RECORD_LEN_1 6
#define USIM_RESPONSE_DATA_FILE_RECORD_LEN_2 7
#define USIM_TYPE_FILE_DES_LEN 5
#define USIM_RESPONSE_DATA_FILE_DES_FLAG 2
#define USIM_RESPONSE_DATA_FILE_DES_LEN_FLAG 3
#define USIM_FILE_DES_TAG 0 x82
#define USIM_FILE_SIZE_TAG 0 x80
#define SIM_RESPONSE_EF_SIZE 15
#define SIM_RESPONSE_DATA_FILE_SIZE_1 2
#define SIM_RESPONSE_DATA_FILE_SIZE_2 3
#define SIM_RESPONSE_DATA_FILE_TYPE 6
#define SIM_RESPONSE_DATA_STRUCTURE 13
#define SIM_RESPONSE_DATA_RECORD_LENGTH 14
#define SIM_TYPE_EF 4
enum class UsimEfType {
TRANSPARENT = 1 ,
LINEAR_FIXED = 2 ,
CYCLIC = 6 ,
};
// 62 17 82 02 41 2183022FE28A01058B032F06038002000A880110
bool convertUsimToSim(const std::vector<uint8_t>& bytesUSIM, std::string* hexSIM) {
const size_t sz = bytesUSIM.size();
size_t i = 0 ;
size_t desIndex;
while (true ) {
if (bytesUSIM[i] == USIM_FILE_DES_TAG) {
desIndex = i;
break ;
} else {
++i;
if (i >= sz) {
return false ;
}
}
}
size_t sizeIndex;
while (true ) {
if (bytesUSIM[i] == USIM_FILE_SIZE_TAG) {
sizeIndex = i;
break ;
} else {
i += bytesUSIM[i + 1 ] + 2 ;
if (i >= sz) {
return FAILURE(false );
}
}
}
uint8_t bytesSIM[SIM_RESPONSE_EF_SIZE] = {0 };
switch (static_cast <UsimEfType>(bytesUSIM[desIndex + USIM_RESPONSE_DATA_FILE_DES_FLAG] & 0 x07)) {
case UsimEfType::TRANSPARENT:
bytesSIM[SIM_RESPONSE_DATA_STRUCTURE] = 0 ;
break ;
case UsimEfType::LINEAR_FIXED:
if (USIM_FILE_DES_TAG != bytesUSIM[USIM_RESPONSE_DATA_FILE_DES_FLAG]) {
return FAILURE(false );
}
if (USIM_TYPE_FILE_DES_LEN != bytesUSIM[USIM_RESPONSE_DATA_FILE_DES_LEN_FLAG]) {
return FAILURE(false );
}
bytesSIM[SIM_RESPONSE_DATA_STRUCTURE] = 1 ;
bytesSIM[SIM_RESPONSE_DATA_RECORD_LENGTH] =
//(byteUSIM[USIM_RESPONSE_DATA_FILE_RECORD_LEN_1] << 8) +
bytesUSIM[USIM_RESPONSE_DATA_FILE_RECORD_LEN_2];
break ;
case UsimEfType::CYCLIC:
bytesSIM[SIM_RESPONSE_DATA_STRUCTURE] = 3 ;
bytesSIM[SIM_RESPONSE_DATA_RECORD_LENGTH] =
//(byteUSIM[USIM_RESPONSE_DATA_FILE_RECORD_LEN_1] << 8) +
bytesUSIM[USIM_RESPONSE_DATA_FILE_RECORD_LEN_2];
break ;
default :
return false ;
}
bytesSIM[SIM_RESPONSE_DATA_FILE_TYPE] = SIM_TYPE_EF;
bytesSIM[SIM_RESPONSE_DATA_FILE_SIZE_1] =
bytesUSIM[sizeIndex + USIM_DATA_OFFSET_2];
bytesSIM[SIM_RESPONSE_DATA_FILE_SIZE_2] =
bytesUSIM[sizeIndex + USIM_DATA_OFFSET_3];
*hexSIM = bin2hex(bytesSIM, sizeof (bytesSIM));
return true ;
}
std::optional<int > getRemainingRetries(const std::string_view pinType,
const AtChannel::RequestPipe requestPipe,
AtChannel::Conversation& atConversation) {
using CPINR = AtResponse::CPINR;
AtResponsePtr response =
atConversation(requestPipe, std::format("AT+CPINR=\" {0 :s}\"" , pinType),
[](const AtResponse& response) -> bool {
return response.holds<CPINR>();
});
if (!response || response->isParseError()) {
return FAILURE(std::nullopt);
} else if (const CPINR* cpinr = response->get_if<CPINR>()) {
return cpinr->remainingRetryTimes;
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, __func__);
}
}
std::pair<RadioError, int > enterOrChangeSimPinPuk(const bool change,
const std::string_view oldPin,
const std::string_view newPin,
const std::string_view pinType,
const AtChannel::RequestPipe requestPipe,
AtChannel::Conversation& atConversation) {
using CmeError = AtResponse::CmeError;
std::string request;
if (change) {
if (pinType.compare("SIM PIN2" sv) == 0 ) {
request = std::format("AT+CPWD=\" {0 :s}\",\" {1 :s}\",\" {2 :s}\"" ,
"P2" sv, oldPin, newPin);
} else {
request = std::format("AT+CPIN={0:s},{1:s}" , oldPin, newPin);
}
} else {
request = std::format("AT+CPIN={0:s}" , oldPin);
}
AtResponsePtr response =
atConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<CmeError>() || response.isOK();
});
if (!response || response->isParseError()) {
return {FAILURE(RadioError::INTERNAL_ERR), 0 };
} else if (response->isOK()) {
return {RadioError::NONE, 0 };
} else if (!response->get_if<CmeError>()) {
response->unexpected(FAILURE_DEBUG_PREFIX, __func__);
}
const std::optional<int > maybeRetries =
getRemainingRetries(pinType, requestPipe, atConversation);
if (maybeRetries) {
return {RadioError::PASSWORD_INCORRECT, maybeRetries.value()};
} else {
return {FAILURE(RadioError::INTERNAL_ERR), 0 };
}
}
// authData64 = base64([randLen][...rand...][authLen][...auth...])
std::tuple<RadioError, std::vector<uint8_t>, std::vector<uint8_t>>
parseAuthData(const AuthContext authContext, const std::string_view authData64) {
auto maybeAuthData = base64decode(authData64.data(), authData64.size());
if (!maybeAuthData) {
return {FAILURE(RadioError::INVALID_ARGUMENTS), {}, {}};
}
const std::vector<uint8_t> authData = std::move(maybeAuthData.value());
const size_t authDataSize = authData.size();
if (authDataSize == 0 ) {
return {FAILURE(RadioError::INVALID_ARGUMENTS), {}, {}};
}
const size_t randLen = authData[0 ];
if (authDataSize < (1 U + randLen)) {
return {FAILURE(RadioError::INVALID_ARGUMENTS), {}, {}};
}
std::vector rand(&authData[1 ], &authData[1 U + randLen]);
if (authContext == AuthContext::SIM) {
return {RadioError::NONE, std::move(rand), {}};
}
const size_t authLen = authData[1 U + randLen];
if (authDataSize < (1 U + randLen + 1 U + authLen)) {
return {FAILURE(RadioError::INVALID_ARGUMENTS), {}, {}};
}
std::vector auth(&authData[1 U + randLen + 1 U],
&authData[1 U + randLen + 1 U + authLen]);
if (authContext == AuthContext::AKA) {
return {RadioError::NONE, std::move(rand), std::move(auth)};
}
return {FAILURE(RadioError::REQUEST_NOT_SUPPORTED), {}, {}};
}
std::optional<std::vector<uint8_t>> getSelectResponse(const AtChannel::RequestPipe requestPipe,
AtChannel::Conversation& atConversation,
const int channel, const int p2) {
using CGLA = AtResponse::CGLA;
using CmeError = AtResponse::CmeError;
const std::string request =
std::format("AT+CGLA={0:d},14,00A400{1:02X}023F00" , channel, p2);
AtResponsePtr response =
atConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<CGLA>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
return FAILURE(std::nullopt);
} else if (const CGLA* cgla = response->get_if<CGLA>()) {
if (cgla->response.size() < 4 ) {
return FAILURE(std::nullopt);
}
int sw12;
const size_t size4 = cgla->response.size() - 4 ;
if (1 != ::sscanf(&cgla->response[size4], "%04x" , &sw12)) {
return FAILURE(std::nullopt);
}
if (sw12 != 0 x9000) {
return FAILURE(std::nullopt);
}
std::vector<uint8_t> selectResponse;
if (!hex2bin(cgla->response, &selectResponse)) {
return FAILURE(std::nullopt);
}
return selectResponse;
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, __func__, __LINE__);
return FAILURE(std::nullopt);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, __func__);
}
}
} // namespace
RadioSim::RadioSim(std::shared_ptr<AtChannel> atChannel) : mAtChannel(std::move(atChannel)) {
}
ScopedAStatus RadioSim::areUiccApplicationsEnabled(const int32_t serial) {
using modem::RadioState;
RadioError status;
{
std::lock_guard<std::mutex> lock(mMtx);
if (mRadioState == RadioState::OFF) {
status = RadioError::RADIO_NOT_AVAILABLE;
} else if (mCardPowerState == sim::CardPowerState::POWER_DOWN) {
status = RadioError::INVALID_SIM_STATE;
} else {
status = RadioError::NONE;
}
}
NOT_NULL(mRadioSimResponse)->areUiccApplicationsEnabledResponse(
makeRadioResponseInfo(serial, status), mUiccApplicationsEnabled);
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::changeIccPin2ForApp(int32_t serial,
const std::string& oldPin2,
const std::string& newPin2,
const std::string& /*aid*/) {
mAtChannel->queueRequester([this , serial, oldPin2, newPin2]
(const AtChannel::RequestPipe requestPipe) -> bool {
const auto [status, remainingRetries] =
enterOrChangeSimPinPuk(true , oldPin2, newPin2, "SIM PIN2" sv,
requestPipe, mAtConversation);
NOT_NULL(mRadioSimResponse)->supplyIccPin2ForAppResponse(
makeRadioResponseInfo(serial, status), remainingRetries);
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::changeIccPinForApp(const int32_t serial,
const std::string& oldPin,
const std::string& newPin,
const std::string& /*aid*/) {
mAtChannel->queueRequester([this , serial, oldPin, newPin]
(const AtChannel::RequestPipe requestPipe) -> bool {
const auto [status, remainingRetries] =
enterOrChangeSimPinPuk(true , oldPin, newPin, "SIM PIN" sv,
requestPipe, mAtConversation);
NOT_NULL(mRadioSimResponse)->changeIccPinForAppResponse(
makeRadioResponseInfo(serial, status), remainingRetries);
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::enableUiccApplications(const int32_t serial, const bool enable) {
bool changed;
{
std::lock_guard<std::mutex> lock(mMtx);
changed = mUiccApplicationsEnabled != enable;
mUiccApplicationsEnabled = enable;
}
NOT_NULL(mRadioSimResponse)->enableUiccApplicationsResponse(
makeRadioResponseInfo(serial));
if (changed && mRadioSimIndication) {
mRadioSimIndication->uiccApplicationsEnablementChanged(
RadioIndicationType::UNSOLICITED, enable);
}
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::getAllowedCarriers(const int32_t serial) {
// This is how it was done in the previous implementation.
using sim::Carrier;
using sim::CarrierInfo;
using sim::CarrierRestrictions;
Carrier allowedCarrier = {
.mcc = "123" ,
.mnc = "456" ,
.matchType = Carrier::MATCH_TYPE_ALL,
};
CarrierInfo allowedCarrierInfo = {
.mcc = allowedCarrier.mcc,
.mnc = allowedCarrier.mnc,
};
CarrierRestrictions carrierRestrictions = {
.allowedCarriers = { std::move(allowedCarrier) },
.allowedCarriersPrioritized = true ,
.allowedCarrierInfoList = { std::move(allowedCarrierInfo) },
};
NOT_NULL(mRadioSimResponse)->getAllowedCarriersResponse(
makeRadioResponseInfo(serial),
std::move(carrierRestrictions),
sim::SimLockMultiSimPolicy::NO_MULTISIM_POLICY);
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::getCdmaSubscription(const int32_t serial) {
NOT_NULL(mRadioSimResponse)->getCdmaSubscriptionResponse(
makeRadioResponseInfo(serial),
"8587777777" , // mdn
"1" , // sid
"1" , // nid
"8587777777" , // min
"1" ); // prl
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::getCdmaSubscriptionSource(const int32_t serial) {
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial](const AtChannel::RequestPipe requestPipe) -> bool {
using CCSS = AtResponse::CCSS;
AtResponsePtr response =
mAtConversation(requestPipe, atCmds::getCdmaSubscriptionSource,
[](const AtResponse& response) -> bool {
return response.holds<CCSS>();
});
if (!response || response->isParseError()) {
NOT_NULL(mRadioSimResponse)->getCdmaSubscriptionSourceResponse(
makeRadioResponseInfo(serial, RadioError::INTERNAL_ERR), {});
return false ;
} else if (const CCSS* csss = response->get_if<CCSS>()) {
NOT_NULL(mRadioSimResponse)->getCdmaSubscriptionSourceResponse(
makeRadioResponseInfo(serial), csss->source);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
return true ;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::getFacilityLockForApp(const int32_t serial, const std::string& facility,
const std::string& password, const int32_t serviceClass,
const std::string& /*appId*/) {
std::string request = std::format("AT+CLCK=\" {0 :s}\",{1:d},\" {2 :s}\",{3:d}" ,
facility, atCmds::kClckQuery, password, serviceClass);
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, request = std::move(request)]
(const AtChannel::RequestPipe requestPipe) -> bool {
using CmeError = AtResponse::CmeError;
using CLCK = AtResponse::CLCK;
RadioError status = RadioError::NONE;
int lockBitmask = 0 ;
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<CLCK>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const CLCK* clck = response->get_if<CLCK>()) {
lockBitmask = clck->locked ? 7 : 0 ;
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
status = cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
NOT_NULL(mRadioSimResponse)->getFacilityLockForAppResponse(
makeRadioResponseInfo(serial, status), lockBitmask);
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::getIccCardStatus(const int32_t serial) {
using sim::AppStatus;
using sim::PersoSubstate;
using sim::PinState;
struct AppStatus3 {
AppStatus usim;
AppStatus ruim;
AppStatus isim;
};
static const std::string kAidPtr = "" ; //"A0000000871002FF86FF0389FFFFFFFF";
static const std::string kAppLabelPtr = "" ;
static const std::string kATR = "" ; //"3BF000818000";
// This data is mandatory and applicable only when cardState is
// STATE_PRESENT and SIM card supports eUICC.
static const std::string kEID = "" ;
static const AppStatus3 kIccStatusReady = {
.usim = {
AppStatus::APP_TYPE_USIM, AppStatus::APP_STATE_READY, PersoSubstate::READY,
kAidPtr, kAppLabelPtr, false , PinState::UNKNOWN, PinState::UNKNOWN
},
.ruim = {
AppStatus::APP_TYPE_RUIM, AppStatus::APP_STATE_READY, PersoSubstate::READY,
kAidPtr, kAppLabelPtr, false , PinState::UNKNOWN, PinState::UNKNOWN
},
.isim = {
AppStatus::APP_TYPE_ISIM, AppStatus::APP_STATE_READY, PersoSubstate::READY,
kAidPtr, kAppLabelPtr, false , PinState::UNKNOWN, PinState::UNKNOWN
}
};
static const AppStatus3 kIccStatusPIN = {
.usim = {
AppStatus::APP_TYPE_USIM, AppStatus::APP_STATE_PIN, PersoSubstate::UNKNOWN,
kAidPtr, kAppLabelPtr, false , PinState::ENABLED_NOT_VERIFIED, PinState::ENABLED_NOT_VERIFIED
},
.ruim = {
AppStatus::APP_TYPE_RUIM, AppStatus::APP_STATE_PIN, PersoSubstate::UNKNOWN,
kAidPtr, kAppLabelPtr, false , PinState::ENABLED_NOT_VERIFIED, PinState::ENABLED_NOT_VERIFIED
},
.isim = {
AppStatus::APP_TYPE_ISIM, AppStatus::APP_STATE_PIN, PersoSubstate::UNKNOWN,
kAidPtr, kAppLabelPtr, false , PinState::ENABLED_NOT_VERIFIED, PinState::ENABLED_NOT_VERIFIED
}
};
static const AppStatus3 kIccStatusPUK = {
.usim = {
AppStatus::APP_TYPE_USIM, AppStatus::APP_STATE_PUK, PersoSubstate::UNKNOWN,
kAidPtr, kAppLabelPtr, false , PinState::ENABLED_NOT_VERIFIED, PinState::ENABLED_NOT_VERIFIED
},
.ruim = {
AppStatus::APP_TYPE_RUIM, AppStatus::APP_STATE_PUK, PersoSubstate::UNKNOWN,
kAidPtr, kAppLabelPtr, false , PinState::ENABLED_NOT_VERIFIED, PinState::ENABLED_NOT_VERIFIED
},
.isim = {
AppStatus::APP_TYPE_ISIM, AppStatus::APP_STATE_PUK, PersoSubstate::UNKNOWN,
kAidPtr, kAppLabelPtr, false , PinState::ENABLED_NOT_VERIFIED, PinState::ENABLED_NOT_VERIFIED
}
};
static const AppStatus3 kIccStatusBUSY = {
.usim = {
AppStatus::APP_TYPE_USIM, AppStatus::APP_STATE_DETECTED, PersoSubstate::UNKNOWN,
kAidPtr, kAppLabelPtr, false , PinState::UNKNOWN, PinState::UNKNOWN
},
.ruim = {
AppStatus::APP_TYPE_RUIM, AppStatus::APP_STATE_DETECTED, PersoSubstate::UNKNOWN,
kAidPtr, kAppLabelPtr, false , PinState::UNKNOWN, PinState::UNKNOWN
},
.isim = {
AppStatus::APP_TYPE_ISIM, AppStatus::APP_STATE_DETECTED, PersoSubstate::UNKNOWN,
kAidPtr, kAppLabelPtr, false , PinState::UNKNOWN, PinState::UNKNOWN
}
};
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial](const AtChannel::RequestPipe requestPipe) -> bool {
using sim::CardStatus;
using CmeError = AtResponse::CmeError;
using CPIN = AtResponse::CPIN;
RadioError status = RadioError::NONE;
CardStatus cardStatus = {
.slotMap = {
.physicalSlotId = -1 , // see ril_service.cpp in CF
.portId = 0 ,
}
};
const AppStatus3* appStatus = nullptr;
AtResponsePtr response =
mAtConversation(requestPipe, atCmds::getSimCardStatus,
[](const AtResponse& response) -> bool {
return response.holds<CPIN>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
goto failed;
} else if (const CPIN* cpin = response->get_if<CPIN>()) {
switch (cpin->state) {
case CPIN::State::READY:
cardStatus.cardState = sim::CardStatus::STATE_PRESENT;
cardStatus.universalPinState = sim::PinState::UNKNOWN;
appStatus = &kIccStatusReady;
break ;
case CPIN::State::PIN:
cardStatus.cardState = sim::CardStatus::STATE_RESTRICTED;
cardStatus.universalPinState = sim::PinState::ENABLED_NOT_VERIFIED;
appStatus = &kIccStatusPIN;
break ;
case CPIN::State::PUK:
cardStatus.cardState = sim::CardStatus::STATE_RESTRICTED;
cardStatus.universalPinState = sim::PinState::ENABLED_NOT_VERIFIED;
appStatus = &kIccStatusPUK;
break ;
default :
status = FAILURE(RadioError::INTERNAL_ERR);
goto failed;
}
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
switch (cmeError->error) {
case RadioError::SIM_ABSENT:
cardStatus.cardState = sim::CardStatus::STATE_ABSENT;
cardStatus.universalPinState = sim::PinState::UNKNOWN;
break ;
case RadioError::SIM_BUSY:
case RadioError::SIM_ERR:
cardStatus.cardState = sim::CardStatus::STATE_ERROR;
cardStatus.universalPinState = sim::PinState::UNKNOWN;
appStatus = &kIccStatusBUSY;
break ;
default :
status = cmeError->getErrorAndLog(
FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
goto failed;
}
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
if (cardStatus.cardState != sim::CardStatus::STATE_ABSENT) {
response =
mAtConversation(requestPipe, atCmds::getICCID,
[](const AtResponse& response) -> bool {
return response.holds<std::string>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
goto failed;
} else if (const std::string* iccid = response->get_if<std::string>()) {
cardStatus.iccid = *iccid;
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
cardStatus.applications.push_back(appStatus->usim);
cardStatus.applications.push_back(appStatus->ruim);
cardStatus.applications.push_back(appStatus->isim);
cardStatus.gsmUmtsSubscriptionAppIndex = 0 ; // usim
cardStatus.cdmaSubscriptionAppIndex = 1 ; // ruim
cardStatus.imsSubscriptionAppIndex = 2 ; // isim
cardStatus.atr = kATR;
cardStatus.eid = kEID;
}
if (status == RadioError::NONE) {
{
std::lock_guard<std::mutex> lock(mMtx);
if (mCardPowerState == sim::CardPowerState::POWER_DOWN) {
cardStatus.applications.clear();
cardStatus.gsmUmtsSubscriptionAppIndex = -1 ;
cardStatus.cdmaSubscriptionAppIndex = -1 ;
cardStatus.imsSubscriptionAppIndex = -1 ;
}
}
NOT_NULL(mRadioSimResponse)->getIccCardStatusResponse(
makeRadioResponseInfo(serial), std::move(cardStatus));
return true ;
} else {
failed: NOT_NULL(mRadioSimResponse)->getIccCardStatusResponse(
makeRadioResponseInfo(serial, status), {});
return status != RadioError::INTERNAL_ERR;
}
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::getImsiForApp(const int32_t serial, const std::string& /*aid*/) {
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial](const AtChannel::RequestPipe requestPipe) -> bool {
using CmeError = AtResponse::CmeError;
RadioError status = RadioError::NONE;
std::string imsi;
AtResponsePtr response =
mAtConversation(requestPipe, atCmds::getIMSI,
[](const AtResponse& response) -> bool {
return response.holds<std::string>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const std::string* pImsi = response->get_if<std::string>()) {
imsi = *pImsi;
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
status = cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
if (status == RadioError::NONE) {
NOT_NULL(mRadioSimResponse)->getImsiForAppResponse(
makeRadioResponseInfo(serial), std::move(imsi));
return true ;
} else {
NOT_NULL(mRadioSimResponse)->getImsiForAppResponse(
makeRadioResponseInfo(serial, FAILURE(status)), {});
return status != RadioError::INTERNAL_ERR;
}
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::getSimPhonebookCapacity(const int32_t serial) {
NOT_NULL(mRadioSimResponse)->getSimPhonebookCapacityResponse(
makeRadioResponseInfoUnsupported( // matches reference-ril.c
serial, FAILURE_DEBUG_PREFIX, __func__), {});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::getSimPhonebookRecords(const int32_t serial) {
NOT_NULL(mRadioSimResponse)->getSimPhonebookRecordsResponse(
makeRadioResponseInfoUnsupported( // matches reference-ril.c
serial, FAILURE_DEBUG_PREFIX, __func__));
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::iccCloseLogicalChannelWithSessionInfo(const int32_t serial,
const sim::SessionInfo& recordInfo) {
const int32_t sessionId = recordInfo.sessionId;
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, sessionId](const AtChannel::RequestPipe requestPipe) -> bool {
using CCHC = AtResponse::CCHC;
using CmeError = AtResponse::CmeError;
RadioError status = RadioError::NONE;
const std::string request = std::format("AT+CCHC={0:d}" , sessionId);
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<CCHC>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
status = cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
if (status == RadioError::NO_SUCH_ELEMENT) {
status = RadioError::INVALID_ARGUMENTS;
}
} else if (!response->get_if<CCHC>()) {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
NOT_NULL(mRadioSimResponse)->iccCloseLogicalChannelWithSessionInfoResponse(
makeRadioResponseInfo(serial, status));
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::iccIoForApp(const int32_t serial, const sim::IccIo& iccIo) {
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, iccIo]
(const AtChannel::RequestPipe requestPipe) -> bool {
using CRSM = AtResponse::CRSM;
using CmeError = AtResponse::CmeError;
using sim::IccIoResult;
RadioError status = RadioError::NONE;
IccIoResult iccIoResult;
std::string request;
if (iccIo.data.empty()) {
request = std::format("AT+CRSM={0:d},{1:d},{2:d},{3:d},{4:d}" ,
iccIo.command, iccIo.fileId, iccIo.p1, iccIo.p2, iccIo.p3);
} else {
request = std::format("AT+CRSM={0:d},{1:d},{2:d},{3:d},{4:d},{5:s},{6:s}" ,
iccIo.command, iccIo.fileId, iccIo.p1, iccIo.p2, iccIo.p3,
iccIo.data, iccIo.aid);
}
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<CRSM>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const CRSM* crsm = response->get_if<CRSM>()) {
iccIoResult.sw1 = crsm->sw1;
iccIoResult.sw2 = crsm->sw2;
if (iccIo.command == 192 ) { // get
std::vector<uint8_t> bytes;
if (hex2bin(crsm->response, &bytes) && !bytes.empty() && (bytes.front() == 0 x62)) {
if (!convertUsimToSim(bytes, &iccIoResult.simResponse)) {
status = FAILURE(RadioError::GENERIC_FAILURE);
}
} else {
iccIoResult.simResponse = crsm->response;
}
} else {
iccIoResult.simResponse = crsm->response;
}
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
status = cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
if (status == RadioError::NONE) {
NOT_NULL(mRadioSimResponse)->iccIoForAppResponse(
makeRadioResponseInfo(serial), std::move(iccIoResult));
return true ;
} else {
NOT_NULL(mRadioSimResponse)->iccIoForAppResponse(
makeRadioResponseInfo(serial, status), {});
return status != RadioError::INTERNAL_ERR;
}
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::iccOpenLogicalChannel(const int32_t serial,
const std::string& aid,
const int32_t p2) {
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, aid, p2](const AtChannel::RequestPipe requestPipe) -> bool {
using CSIM = AtResponse::CSIM;
using CmeError = AtResponse::CmeError;
RadioError status = RadioError::NONE;
int channelId = 0 ;
std::vector<uint8_t> selectResponse;
if (aid.empty()) {
AtResponsePtr response =
mAtConversation(requestPipe, "AT+CSIM=10,\" 0070000001 \"" sv,
[](const AtResponse& response) -> bool {
return response.holds<CSIM>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const CSIM* csim = response->get_if<CSIM>()) {
if (1 == ::sscanf(csim->response.c_str(), "%02x" , &channelId)) {
if (p2 >= 0 ) {
auto maybeSelectResponse =
getSelectResponse(requestPipe, mAtConversation,
channelId, p2);
if (maybeSelectResponse) {
selectResponse = std::move(maybeSelectResponse.value());
} else {
requestPipe(std::format("AT+CCHC={0:d}" , channelId));
status = FAILURE(RadioError::GENERIC_FAILURE);
}
} else {
if (!hex2bin(std::string_view(csim->response).substr(2 ),
&selectResponse)) {
status = FAILURE(RadioError::GENERIC_FAILURE);
}
}
} else {
status = FAILURE(RadioError::GENERIC_FAILURE);
}
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
status = cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
} else {
const std::string request = std::format("AT+CCHO={0:s}" , aid);
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<std::string>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const std::string* idStr = response->get_if<std::string>()) {
const char * end = idStr->data() + idStr->size();
if (std::from_chars(idStr->data(), end, channelId, 10 ).ptr != end) {
status = FAILURE(RadioError::INTERNAL_ERR);
}
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
status = cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
}
NOT_NULL(mRadioSimResponse)->iccOpenLogicalChannelResponse(
makeRadioResponseInfo(serial, status), channelId, std::move(selectResponse));
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::iccTransmitApduBasicChannel(const int32_t serial,
const sim::SimApdu& message) {
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, message]
(const AtChannel::RequestPipe requestPipe) -> bool {
using CSIM = AtResponse::CSIM;
using CmeError = AtResponse::CmeError;
using sim::IccIoResult;
RadioError status = RadioError::NONE;
IccIoResult iccIoResult;
std::string request;
if (message.data.empty()) {
if (message.p3 < 0 ) {
request = std::format(
"AT+CSIM={0:d},\" {1 :02 x}{2 :02 x}{3 :02 x}{4 :02 x}\"" , 8 ,
message.cla, message.instruction, message.p1, message.p2);
} else {
request = std::format(
"AT+CSIM={0:d},\" {1 :02 x}{2 :02 x}{3 :02 x}{4 :02 x}{5 :02 x}\"" , 10 ,
message.cla, message.instruction, message.p1, message.p2, message.p3);
}
} else {
const size_t dataSize = 10 + message.data.size();
request = std::format(
"AT+CSIM={0:d},\" {1 :02 x}{2 :02 x}{3 :02 x}{4 :02 x}{5 :02 x}{6 :s}\"" ,
dataSize, message.cla, message.instruction, message.p1,
message.p2, message.p3, message.data);
}
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<CSIM>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const CSIM* csim = response->get_if<CSIM>()) {
const std::string& simResponse = csim->response;
if (simResponse.size() >= 4 ) {
if (2 == ::sscanf(&simResponse[simResponse.size() - 4 ], "%02X%02X" ,
&iccIoResult.sw1, &iccIoResult.sw2)) {
iccIoResult.simResponse = simResponse.substr(0 , simResponse.size() - 4 );
} else {
status = FAILURE(RadioError::GENERIC_FAILURE);
}
} else {
status = FAILURE(RadioError::GENERIC_FAILURE);
}
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
status = cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
if (status == RadioError::NONE) {
NOT_NULL(mRadioSimResponse)->iccTransmitApduBasicChannelResponse(
makeRadioResponseInfo(serial), std::move(iccIoResult));
return true ;
} else {
NOT_NULL(mRadioSimResponse)->iccTransmitApduBasicChannelResponse(
makeRadioResponseInfo(serial, status), {});
return status != RadioError::INTERNAL_ERR;
}
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::iccTransmitApduLogicalChannel(
const int32_t serial, const sim::SimApdu& message) {
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, message]
(const AtChannel::RequestPipe requestPipe) -> bool {
using CGLA = AtResponse::CGLA;
using CmeError = AtResponse::CmeError;
using sim::IccIoResult;
RadioError status = RadioError::NONE;
IccIoResult iccIoResult;
const size_t dataSize = 10 + message.data.size();
const std::string request = std::format(
"AT+CGLA={0:d},{1:d},{2:02x}{3:02x}{4:02x}{5:02x}{6:02x}{7:s}" ,
message.sessionId, dataSize,
message.cla, message.instruction, message.p1,
message.p2, message.p3, message.data);
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<CGLA>() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const CGLA* cgla = response->get_if<CGLA>()) {
if (cgla->response.size() >= 4 ) {
const size_t size4 = cgla->response.size() - 4 ;
if (2 == ::sscanf(&cgla->response[size4], "%02x%02x" ,
&iccIoResult.sw1, &iccIoResult.sw2)) {
iccIoResult.simResponse = cgla->response.substr(0 , size4);
} else {
status = FAILURE(RadioError::GENERIC_FAILURE);
}
} else {
status = FAILURE(RadioError::GENERIC_FAILURE);
}
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
status = cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
if (status == RadioError::NONE) {
NOT_NULL(mRadioSimResponse)->iccTransmitApduLogicalChannelResponse(
makeRadioResponseInfo(serial), std::move(iccIoResult));
return true ;
} else {
NOT_NULL(mRadioSimResponse)->iccTransmitApduLogicalChannelResponse(
makeRadioResponseInfo(serial, status), {});
return status != RadioError::INTERNAL_ERR;
}
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::reportStkServiceIsRunning(const int32_t serial) {
decltype(mStkUnsolResponse) stkUnsolResponse;
{
std::lock_guard<std::mutex> lock(mMtx);
mStkServiceRunning = true ;
stkUnsolResponse = std::move(mStkUnsolResponse);
}
if (stkUnsolResponse) {
NOT_NULL(mRadioSimIndication)->stkProactiveCommand(
RadioIndicationType::UNSOLICITED, std::move(stkUnsolResponse.value().cmd));
}
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial]
(const AtChannel::RequestPipe requestPipe) -> bool {
using CUSATD = AtResponse::CUSATD;
RadioError status = RadioError::NONE;
AtResponsePtr response =
mAtConversation(requestPipe, atCmds::reportStkServiceRunning,
[](const AtResponse& response) -> bool {
return response.holds<CUSATD>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (!response->get_if<CUSATD>()) {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
NOT_NULL(mRadioSimResponse)->reportStkServiceIsRunningResponse(
makeRadioResponseInfo(serial, status));
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::requestIccSimAuthentication(const int32_t serial,
const int32_t authContextInt,
const std::string& authData64,
const std::string& /*aid*/) {
const AuthContext authContext = static_cast <AuthContext>(authContextInt);
auto [status, randBin, authBin] = parseAuthData(authContext, authData64);
if (status != RadioError::NONE) {
NOT_NULL(mRadioSimResponse)->requestIccSimAuthenticationResponse(
makeRadioResponseInfo(serial, status), {});
return ScopedAStatus::ok();
}
std::string randHex = bin2hex(randBin.data(), randBin.size());
std::string authHex = bin2hex(authBin.data(), authBin.size());
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, authContext,
randHex = std::move(randHex),
authHex = std::move(authHex)]
(const AtChannel::RequestPipe requestPipe) -> bool {
using CmeError = AtResponse::CmeError;
using MBAU = AtResponse::MBAU;
using sim::IccIoResult;
RadioError status = RadioError::NONE;
IccIoResult iccIoResult;
std::string request;
switch (authContext) {
case AuthContext::SIM:
request = std::format("AT^MBAU=\" {0 :s}\"" , randHex);
break ;
case AuthContext::AKA:
request = std::format("AT^MBAU=\" {0 :s},{1 :s}\"" , randHex, authHex); // the quotes are interesting here
break ;
default :
return FAILURE(false );
}
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<MBAU>() ||
response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const MBAU* mbau = response->get_if<MBAU>()) {
const auto putByte = [](uint8_t* dst, uint8_t b) -> uint8_t* {
*dst = b;
return dst + 1 ;
};
const auto putRange = [](uint8_t* dst, const uint8_t* src, size_t size) -> uint8_t* {
memcpy(dst, src, size);
return dst + size;
};
const auto putSizedRange = [putByte, putRange](uint8_t* dst, const uint8_t* src, size_t size) -> uint8_t* {
return putRange(putByte(dst, size), src, size);
};
std::vector<uint8_t> responseBin;
uint8_t* p;
switch (authContext) {
case AuthContext::SIM: // sresLen + sres + kcLen + kc
responseBin.resize(2 + mbau->sres.size() + mbau->kc.size());
p = responseBin.data();
p = putSizedRange(p, mbau->sres.data(), mbau->sres.size());
p = putSizedRange(p, mbau->kc.data(), mbau->kc.size());
break ;
case AuthContext::AKA: // 0xDB + ckLen + ck + ikLen + ik + resAutsLen + resAuts
responseBin.resize(4 + mbau->ck.size() + mbau->ik.size() + mbau->resAuts.size());
p = responseBin.data();
p = putByte(p, 0 xDB);
p = putSizedRange(p, mbau->ck.data(), mbau->ck.size());
p = putSizedRange(p, mbau->ik.data(), mbau->ik.size());
p = putSizedRange(p, mbau->resAuts.data(), mbau->resAuts.size());
break ;
}
iccIoResult.sw1 = 0 x90;
iccIoResult.sw2 = 0 ;
iccIoResult.simResponse = base64encode(responseBin.data(), responseBin.size());
} else if (response->isOK()) {
status = FAILURE(RadioError::GENERIC_FAILURE);
} else if (const CmeError* cmeError = response->get_if<CmeError>()) {
status = cmeError->getErrorAndLog(FAILURE_DEBUG_PREFIX, kFunc, __LINE__);
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
if (status == RadioError::NONE) {
NOT_NULL(mRadioSimResponse)->requestIccSimAuthenticationResponse(
makeRadioResponseInfo(serial), std::move(iccIoResult));
return true ;
} else {
NOT_NULL(mRadioSimResponse)->requestIccSimAuthenticationResponse(
makeRadioResponseInfo(serial, status), {});
return status != RadioError::INTERNAL_ERR;
}
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::sendEnvelope(const int32_t serial,
const std::string& contents) {
if (contents.empty()) {
NOT_NULL(mRadioSimResponse)->sendEnvelopeResponse(
makeRadioResponseInfo(serial, RadioError::INVALID_ARGUMENTS), {});
return ScopedAStatus::ok();
}
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, contents]
(const AtChannel::RequestPipe requestPipe) -> bool {
using CUSATE = AtResponse::CUSATE;
RadioError status = RadioError::NONE;
std::string commandResponse;
const std::string request = std::format("AT+CUSATE=\" {0 :s}\"" , contents);
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<CUSATE>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (const CUSATE* cusate = response->get_if<CUSATE>()) {
commandResponse = cusate->response;
} else {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
NOT_NULL(mRadioSimResponse)->sendEnvelopeResponse(
makeRadioResponseInfo(serial, status), std::move(commandResponse));
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::sendEnvelopeWithStatus(const int32_t serial,
const std::string& /*contents*/) {
NOT_NULL(mRadioSimResponse)->sendEnvelopeWithStatusResponse(
makeRadioResponseInfoUnsupported( // matches reference-ril.c
serial, FAILURE_DEBUG_PREFIX, __func__), {});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::sendTerminalResponseToSim(const int32_t serial,
const std::string& commandResponse) {
if (commandResponse.empty()) {
NOT_NULL(mRadioSimResponse)->sendTerminalResponseToSimResponse(
makeRadioResponseInfo(serial, RadioError::INVALID_ARGUMENTS));
return ScopedAStatus::ok();
}
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, commandResponse]
(const AtChannel::RequestPipe requestPipe) -> bool {
using CUSATT = AtResponse::CUSATT;
RadioError status = RadioError::NONE;
const std::string request = std::format("AT+CUSATT=\" {0 :s}\"" , commandResponse);
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.holds<CUSATT>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (!response->get_if<CUSATT>()) {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
NOT_NULL(mRadioSimResponse)->sendTerminalResponseToSimResponse(
makeRadioResponseInfo(serial, status));
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::setAllowedCarriers(const int32_t serial,
const sim::CarrierRestrictions& /*carriers*/,
const sim::SimLockMultiSimPolicy /*multiSimPolicy*/) {
NOT_NULL(mRadioSimResponse)->setAllowedCarriersResponse(
makeRadioResponseInfoNOP(serial));
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::setCarrierInfoForImsiEncryption(const int32_t serial,
const sim::ImsiEncryptionInfo& /*imsiEncryptionInfo*/) {
NOT_NULL(mRadioSimResponse)->setCarrierInfoForImsiEncryptionResponse(
makeRadioResponseInfoUnsupported( // matches reference-ril.c
serial, FAILURE_DEBUG_PREFIX, __func__));
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::setCdmaSubscriptionSource(const int32_t serial,
const sim::CdmaSubscriptionSource cdmaSub) {
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, cdmaSub]
(const AtChannel::RequestPipe requestPipe) -> bool {
RadioError status = RadioError::NONE;
const std::string request =
std::format("AT+CCSS={0:d}" , static_cast <unsigned >(cdmaSub));
const AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.isOK();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (!response->isOK()) {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
NOT_NULL(mRadioSimResponse)->setCdmaSubscriptionSourceResponse(
makeRadioResponseInfo(serial, status));
if ((status == RadioError::NONE) && mRadioSimIndication) {
mRadioSimIndication->cdmaSubscriptionSourceChanged(
RadioIndicationType::UNSOLICITED, cdmaSub);
}
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::setFacilityLockForApp(const int32_t serial,
const std::string& facility,
const bool lockState,
const std::string& passwd,
const int32_t serviceClass,
const std::string& /*appId*/) {
static const char * const kFunc = __func__;
mAtChannel->queueRequester([this , serial, facility, lockState,
passwd, serviceClass]
(const AtChannel::RequestPipe requestPipe) -> bool {
using CmeError = AtResponse::CmeError;
RadioError status = RadioError::NONE;
int retry = 1 ;
const int lockStateInt = lockState ? 1 : 0 ;
std::string request;
if (serviceClass == 0 ) {
request = std::format("AT+CLCK=\" {0 :s}\",{1:d},\" {2 :s}\"" ,
facility, lockStateInt, passwd);
} else {
request = std::format("AT+CLCK=\" {0 :s}\",{1:d},\" {2 :s}\",{3:d}" ,
facility, lockStateInt, passwd, serviceClass);
}
AtResponsePtr response =
mAtConversation(requestPipe, request,
[](const AtResponse& response) -> bool {
return response.isOK() || response.holds<CmeError>();
});
if (!response || response->isParseError()) {
status = FAILURE(RadioError::INTERNAL_ERR);
} else if (response->get_if<CmeError>()) {
if (facility.compare("SC" sv) == 0 ) {
const std::optional<int > maybeRetries =
getRemainingRetries("SIM PIN" sv, requestPipe, mAtConversation);
if (maybeRetries) {
status = FAILURE(RadioError::PASSWORD_INCORRECT);
retry = maybeRetries.value();
} else {
status = FAILURE(RadioError::INTERNAL_ERR);
}
} else if (facility.compare("FD" sv) == 0 ) {
const std::optional<int > maybeRetries =
getRemainingRetries("SIM PIN2" sv, requestPipe, mAtConversation);
if (maybeRetries) {
status = FAILURE(RadioError::PASSWORD_INCORRECT);
retry = maybeRetries.value();
} else {
status = FAILURE(RadioError::INTERNAL_ERR);
}
} else {
status = FAILURE(RadioError::INVALID_ARGUMENTS);
retry = -1 ;
}
} else if (!response->isOK()) {
response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
}
NOT_NULL(mRadioSimResponse)->setFacilityLockForAppResponse(
makeRadioResponseInfo(serial, status), retry);
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::setSimCardPower(const int32_t serial,
const sim::CardPowerState powerState) {
{
std::lock_guard<std::mutex> lock(mMtx);
mCardPowerState = powerState;
}
NOT_NULL(mRadioSimResponse)->setSimCardPowerResponse(
makeRadioResponseInfoNOP(serial));
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::setUiccSubscription(const int32_t serial,
const sim::SelectUiccSub& /*uiccSub*/) {
NOT_NULL(mRadioSimResponse)->setUiccSubscriptionResponse(
makeRadioResponseInfoUnsupported( // matches reference-ril.c
serial, FAILURE_DEBUG_PREFIX, __func__));
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::supplyIccPin2ForApp(int32_t serial,
const std::string& pin2,
const std::string& /*aid*/) {
mAtChannel->queueRequester([this , serial, pin2]
(const AtChannel::RequestPipe requestPipe) -> bool {
const auto [status, remainingRetries] =
enterOrChangeSimPinPuk(false , pin2, "" , "SIM PIN2" sv,
requestPipe, mAtConversation);
NOT_NULL(mRadioSimResponse)->supplyIccPin2ForAppResponse(
makeRadioResponseInfo(serial, status), remainingRetries);
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::supplyIccPinForApp(int32_t serial,
const std::string& pin,
const std::string& /*aid*/) {
mAtChannel->queueRequester([this , serial, pin]
(const AtChannel::RequestPipe requestPipe) -> bool {
const auto [status, remainingRetries] =
enterOrChangeSimPinPuk(false , pin, "" , "SIM PIN" sv,
requestPipe, mAtConversation);
NOT_NULL(mRadioSimResponse)->supplyIccPinForAppResponse(
makeRadioResponseInfo(serial, status), remainingRetries);
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::supplyIccPuk2ForApp(int32_t serial,
const std::string& puk2,
const std::string& pin2,
const std::string& /*aid*/) {
mAtChannel->queueRequester([this , serial, puk2, pin2]
(const AtChannel::RequestPipe requestPipe) -> bool {
const auto [status, remainingRetries] =
enterOrChangeSimPinPuk(true , puk2, pin2, "SIM PUK2" sv,
requestPipe, mAtConversation);
NOT_NULL(mRadioSimResponse)->supplyIccPuk2ForAppResponse(
makeRadioResponseInfo(serial, status), remainingRetries);
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::supplyIccPukForApp(const int32_t serial,
const std::string& puk,
const std::string& pin,
const std::string& /*aid*/) {
mAtChannel->queueRequester([this , serial, puk, pin]
(const AtChannel::RequestPipe requestPipe) -> bool {
const auto [status, remainingRetries] =
enterOrChangeSimPinPuk(true , puk, pin, "SIM PUK" sv,
requestPipe, mAtConversation);
NOT_NULL(mRadioSimResponse)->supplyIccPukForAppResponse(
makeRadioResponseInfo(serial, status), remainingRetries);
return status != RadioError::INTERNAL_ERR;
});
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::supplySimDepersonalization(const int32_t serial,
sim::PersoSubstate /*persoType*/,
const std::string& /*controlKey*/) {
NOT_NULL(mRadioSimResponse)->supplySimDepersonalizationResponse(
makeRadioResponseInfoUnsupported( // matches reference-ril.c
serial, FAILURE_DEBUG_PREFIX, __func__),
{}, 0 );
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::updateSimPhonebookRecords(const int32_t serial,
const sim::PhonebookRecordInfo& /*recordInfo*/) {
NOT_NULL(mRadioSimResponse)->updateSimPhonebookRecordsResponse(
makeRadioResponseInfoUnsupported( // matches reference-ril.c
serial, FAILURE_DEBUG_PREFIX, __func__), 0 );
return ScopedAStatus::ok();
}
void RadioSim::handleUnsolicited(const AtResponse::CFUN& cfun) {
bool changed;
{
std::lock_guard<std::mutex> lock(mMtx);
changed = mRadioState != cfun.state;
mRadioState = cfun.state;
if (mRadioState == modem::RadioState::ON) {
mCardPowerState = sim::CardPowerState::POWER_UP;
}
}
if (changed && mRadioSimIndication) {
mRadioSimIndication->simStatusChanged(
RadioIndicationType::UNSOLICITED);
mRadioSimIndication->subscriptionStatusChanged(
RadioIndicationType::UNSOLICITED, mRadioState == modem::RadioState::ON);
}
}
void RadioSim::handleUnsolicited(const AtResponse::CUSATP& cusatp) {
const std::string& cmd = cusatp.cmd;
if (cmd.size() < 3 ) {
return ;
}
const unsigned typeOffset = (cmd[2 ] <= '7' ) ? 10 : 12 ;
if (cmd.size() < (typeOffset + 2 )) {
return ;
}
unsigned cmdType = 0 ;
if (!(std::from_chars(&cmd[typeOffset], &cmd[typeOffset + 2 ], cmdType, 16 ).ec == std::errc{})) {
return ;
}
const StkCmdType stkCmdType = static_cast <StkCmdType>(cmdType);
enum class Action {
NOTHING, NOTIFY, PROACTIVE_CMD
};
Action action;
{
std::lock_guard<std::mutex> lock(mMtx);
switch (stkCmdType) {
case StkCmdType::RUN_AT:
case StkCmdType::SEND_DTMF:
case StkCmdType::SEND_SMS:
case StkCmdType::SEND_SS:
case StkCmdType::SEND_USSD:
case StkCmdType::PLAY_TONE:
case StkCmdType::CLOSE_CHANNEL:
action = Action::NOTIFY;
break ;
case StkCmdType::REFRESH:
if (cmd.size() >= (typeOffset + 4 ) && !strncmp(&cmd[typeOffset + 2 ], "04" , 2 )) {
// SIM_RESET
mStkServiceRunning = false ;
action = Action::NOTHING;
} else {
action = Action::NOTIFY;
}
break ;
default :
action = Action::PROACTIVE_CMD;
break ;
}
if (!mStkServiceRunning) {
mStkUnsolResponse = cusatp;
action = Action::NOTHING;
}
}
if (mRadioSimIndication) {
switch (action) {
case Action::NOTIFY:
mRadioSimIndication->stkEventNotify(RadioIndicationType::UNSOLICITED, cmd);
break ;
case Action::PROACTIVE_CMD:
mRadioSimIndication->stkProactiveCommand(RadioIndicationType::UNSOLICITED, cmd);
break ;
case Action::NOTHING:
break ;
}
}
}
void RadioSim::handleUnsolicited(const AtResponse::CUSATEND&) {
if (mRadioSimIndication) {
mRadioSimIndication->stkSessionEnd(RadioIndicationType::UNSOLICITED);
}
}
void RadioSim::atResponseSink(const AtResponsePtr& response) {
if (!mAtConversation.send(response)) {
response->visit([this ](const auto & msg){ handleUnsolicited(msg); });
}
}
ScopedAStatus RadioSim::responseAcknowledgement() {
return ScopedAStatus::ok();
}
ScopedAStatus RadioSim::setResponseFunctions(
const std::shared_ptr<sim::IRadioSimResponse>& radioSimResponse,
const std::shared_ptr<sim::IRadioSimIndication>& radioSimIndication) {
mRadioSimResponse = NOT_NULL(radioSimResponse);
mRadioSimIndication = NOT_NULL(radioSimIndication);
return ScopedAStatus::ok();
}
/************************* deprecated *************************/
ScopedAStatus RadioSim::iccCloseLogicalChannel(const int32_t serial,
const int32_t /*channelId*/) {
NOT_NULL(mRadioSimResponse)->iccCloseLogicalChannelResponse(
makeRadioResponseInfoDeprecated(serial));
return ScopedAStatus::ok();
}
} // namespace implementation
} // namespace radio
} // namespace hardware
} // namespace android
} // namespace aidl
Messung V0.5 in Prozent C=90 H=94 G=91
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet am 2026-06-27)
¤
*© Formatika GbR, Deutschland