Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  RadioSim.cpp

  Sprache: C
 

/*
 * 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        = 0x34,
    SEND_DTMF     = 0x14,
    SEND_SMS      = 0x13,
    SEND_SS       = 0x11,
    SEND_USSD     = 0x12,
    PLAY_TONE     = 0x20,
    OPEN_CHANNEL  = 0x40,
    CLOSE_CHANNEL = 0x41,
    RECEIVE_DATA  = 0x42,
    SEND_DATA     = 0x43,
    GET_CHANNEL_STATUS = 0x44,
    REFRESH       = 0x01,
};

#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                       0x82
#define USIM_FILE_SIZE_TAG                      0x80


#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] & 0x07)) {
    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 < (1U + randLen)) {
        return {FAILURE(RadioError::INVALID_ARGUMENTS), {}, {}};
    }

    std::vector rand(&authData[1], &authData[1U + randLen]);
    if (authContext == AuthContext::SIM) {
        return {RadioError::NONE, std::move(rand), {}};
    }

    const size_t authLen = authData[1U + randLen];
    if (authDataSize < (1U + randLen + 1U + authLen)) {
        return {FAILURE(RadioError::INVALID_ARGUMENTS), {}, {}};
    }

    std::vector auth(&authData[1U + randLen + 1U],
                     &authData[1U + randLen + 1U + 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 != 0x9000) {
            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 charconst 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&&nbsp;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 charconst 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 charconst 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 charconst 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 charconst 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 charconst 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() == 0x62)) {
                    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 charconst 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 charconst 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:02x}{2:02x}{3:02x}{4:02x}\""8,
                        message.cla, message.instruction, message.p1, message.p2);
            } else {
                request = std::format(
                        "AT+CSIM={0:d},\"{1:02x}{2:02x}{3:02x}{4:02x}{5:02x}\""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:02x}{2:02x}{3:02x}{4:02x}{5:02x}{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 charconst 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 charconst 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 charconst 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, 0xDB);
                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 = 0x90;
            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 charconst 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 charconst 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 charconst 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 charconst 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.26 Sekunden  (vorverarbeitet am  2026-06-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik