blob: 2c691403b620af34b7d4b85fc09df23fa9f6e93d [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <array>
#include <utility>
#include <base/bind.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <glib.h>
#include <gio/gio.h>
#include <gmodule.h>
#include <gio/gunixsocketaddress.h>
#include <libmbim-glib/libmbim-glib.h>
#include "hermes/apdu.h"
#include "hermes/euicc_manager_interface.h"
#include "hermes/hermes_common.h"
#include "hermes/modem_mbim.h"
#include "hermes/sgp_22.h"
#include "hermes/type_traits.h"
namespace {
const guint kMbimResponseTimeout = 30;
constexpr int kMbimMessageSuccess = 144;
// Application identifier for the eUICC's SIM EID
const std::array<uint8_t, 12> kMbimEidReqApdu = {
0x81, 0xE2, 0x91, 0x00, 0x06, 0xBF, 0x3E, 0x03, 0x5C, 0x01, 0x5A, 0x00,
};
} // namespace
namespace hermes {
/* static */
std::unique_ptr<ModemMbim> ModemMbim::Create(Logger* logger,
Executor* executor) {
VLOG(2) << __func__;
GFile* file = NULL;
const gchar* const path = "/dev/cdc-wdm0";
file = g_file_new_for_path(path);
if (file == NULL) {
LOG(ERROR) << __func__ << " :No file exist";
return nullptr;
}
return std::unique_ptr<ModemMbim>(new ModemMbim(file, logger, executor));
}
ModemMbim::ModemMbim(GFile* file, Logger* logger, Executor* executor)
: Modem<MbimCmd>(logger, executor),
logical_slot_(0),
channel_(kInvalidChannel),
pending_response_(false),
ready_state_(MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED),
file_(file),
is_ready_state_valid(false),
weak_factory_(this) {}
ModemMbim::~ModemMbim() {
VLOG(2) << "~ModemMbim Destructor++";
Shutdown();
}
void ModemMbim::Initialize(EuiccManagerInterface* euicc_manager,
ResultCallback cb) {
LOG(INFO) << __func__;
CHECK(current_state_ == State::kMbimUninitialized);
retry_initialization_callback_.Reset();
euicc_manager_ = euicc_manager;
init_done_cb_ = std::move(cb);
current_state_.Transition(State::kMbimInitializeStarted);
mbim_device_new(file_, /* cancellable */ NULL,
(GAsyncReadyCallback)MbimCreateNewDeviceCb, this);
}
void ModemMbim::Shutdown() {
LOG(INFO) << __func__;
if (device_) {
if (g_signal_handler_is_connected(device_.get(), indication_id_)) {
g_signal_handler_disconnect(device_.get(), indication_id_);
}
device_.reset();
}
channel_ = kInvalidChannel;
pending_response_ = false;
ready_state_ = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED,
current_state_.Transition(State::kMbimUninitialized);
}
void ModemMbim::TransmitFromQueue() {
VLOG(2) << __func__ << "++";
if (tx_queue_.empty() || pending_response_ ||
retry_initialization_callback_) {
return;
}
auto mbim_cmd = tx_queue_[0].msg_.get();
switch (mbim_cmd->mbim_type()) {
case MbimCmd::MbimType::kMbimOpenLogicalChannel:
TransmitMbimOpenLogicalChannel();
break;
case MbimCmd::MbimType::kMbimCloseLogicalChannel:
TransmitMbimCloseChannel();
break;
case MbimCmd::MbimType::kMbimSendApdu:
TransmitMbimSendApdu(&tx_queue_[0]);
break;
case MbimCmd::MbimType::kMbimSubscriberStatusReady:
TransmitSubscriberReadyStatusQuery();
break;
case MbimCmd::MbimType::kMbimDeviceCaps:
TransmitMbimLoadCurrentCapabilities();
break;
case MbimCmd::MbimType::kMbimSendEidApdu:
TransmitMbimSendEidApdu();
break;
default:
break;
}
}
std::unique_ptr<MbimCmd> ModemMbim::GetTagForSendApdu() {
return std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimSendApdu);
}
void ModemMbim::ProcessMbimResult(int err) {
if (tx_queue_.empty()) {
VLOG(2) << __func__ << " :Queue is Empty";
return;
}
// pop before running the callback since the callback might change the state
// of the queue.
auto cb_ = std::move(tx_queue_[0].cb_);
tx_queue_.pop_front();
if (!cb_.is_null()) {
std::move(cb_).Run(err);
}
}
/* static */
void ModemMbim::MbimCreateNewDeviceCb(GObject* source,
GAsyncResult* res,
ModemMbim* modem_mbim) {
/* Open the device */
VLOG(2) << __func__;
g_autoptr(GError) error = NULL;
glib_bridge::ScopedGObject<MbimDevice> mbimdevice(
mbim_device_new_finish(res, &error));
modem_mbim->device_ = std::move(mbimdevice);
if (!modem_mbim->device_.get() || error != NULL) {
LOG(WARNING) << "Failed due to error: " << error->message;
modem_mbim->RetryInitialization(std::move(modem_mbim->init_done_cb_));
return;
}
mbim_device_open_full(modem_mbim->device_.get(), MBIM_DEVICE_OPEN_FLAGS_PROXY,
kMbimResponseTimeout, /* cancellable */ NULL,
(GAsyncReadyCallback)MbimDeviceOpenReadyCb, modem_mbim);
}
/* static */
void ModemMbim::MbimDeviceOpenReadyCb(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
VLOG(2) << __func__ << "++";
g_autoptr(GError) error = NULL;
if (!mbim_device_open_finish(device, res, &error)) {
LOG(ERROR) << "Failed due to error: " << error->message;
modem_mbim->RetryInitialization(std::move(modem_mbim->init_done_cb_));
return;
}
modem_mbim->indication_id_ = g_signal_connect(
modem_mbim->device_.get(), MBIM_DEVICE_SIGNAL_INDICATE_STATUS,
G_CALLBACK(ClientIndicationCb), modem_mbim);
LOG(INFO) << "Mbim Device is ready, complete the initialization process";
auto get_imei = base::BindOnce(&ModemMbim::QueryCurrentMbimCapabilities,
modem_mbim->weak_factory_.GetWeakPtr());
modem_mbim->tx_queue_.push_back(
{std::unique_ptr<TxInfo>(), modem_mbim->AllocateId(),
std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimSubscriberStatusReady),
base::BindOnce(&ModemMbim::RunNextStepOrRetry,
modem_mbim->weak_factory_.GetWeakPtr(),
std::move(get_imei),
std::move(modem_mbim->init_done_cb_))});
modem_mbim->TransmitFromQueue();
}
void ModemMbim::TransmitSubscriberReadyStatusQuery() {
g_autoptr(MbimMessage) message = NULL;
VLOG(2) << __func__;
message = mbim_message_subscriber_ready_status_query_new(NULL);
if (!message) {
LOG(ERROR) << "Mbim message creation failed";
ProcessMbimResult(kModemMessageProcessingError);
return;
}
mbim_device_command(device_.get(), message, kMbimResponseTimeout,
/*cancellable*/ NULL,
(GAsyncReadyCallback)SubscriberReadyStatusRspCb, this);
}
void ModemMbim::TransmitMbimLoadCurrentCapabilities() {
g_autoptr(MbimMessage) message = NULL;
VLOG(2) << __func__;
message = mbim_message_device_caps_query_new(/* error */ NULL);
if (!message) {
LOG(ERROR) << __func__ << " :Mbim message creation failed";
ProcessMbimResult(kModemMessageProcessingError);
return;
}
mbim_device_command(device_.get(), message, kMbimResponseTimeout,
/*cancellable*/ NULL,
(GAsyncReadyCallback)DeviceCapsQueryReady, this);
}
void ModemMbim::TransmitMbimCloseChannel() {
g_autoptr(MbimMessage) message = NULL;
g_autoptr(GError) error = NULL;
VLOG(2) << __func__;
message = mbim_message_ms_uicc_low_level_access_close_channel_set_new(
channel_,
/*channelGroupId*/ 1, &error);
if (!message) {
LOG(ERROR) << "Mbim message creation failed:" << error->message;
ProcessMbimResult(kModemMessageProcessingError);
return;
}
pending_response_ = true;
mbim_device_command(device_.get(), message, kMbimResponseTimeout,
/*cancellable*/ NULL,
(GAsyncReadyCallback)UiccLowLevelAccessCloseChannelSetCb,
this);
}
void ModemMbim::TransmitMbimOpenLogicalChannel() {
VLOG(2) << __func__;
guint8 appId[16];
guint32 appIdSize = kAidIsdr.size();
g_autoptr(GError) error = NULL;
g_autoptr(MbimMessage) message = NULL;
std::copy(kAidIsdr.begin(), kAidIsdr.end(), appId);
message = mbim_message_ms_uicc_low_level_access_open_channel_set_new(
appIdSize, appId, /* selectP2arg */ 4, /* channelGroupId */ 1, &error);
if (!message) {
LOG(ERROR) << __func__ << ": Mbim Message Creation Failed";
ProcessMbimResult(kModemMessageProcessingError);
return;
}
pending_response_ = true;
mbim_device_command(device_.get(), message, kMbimResponseTimeout,
/*cancellable*/ NULL,
(GAsyncReadyCallback)UiccLowLevelAccessOpenChannelSetCb,
this);
}
void ModemMbim::TransmitMbimSendEidApdu() {
VLOG(2) << __func__;
uint8_t eid_apduCmd[kMaxApduLen];
guint32 kMbimEidReqApduSize = kMbimEidReqApdu.size();
g_autoptr(MbimMessage) message = NULL;
MbimUiccSecureMessaging secure_messaging = MBIM_UICC_SECURE_MESSAGING_NONE;
MbimUiccClassByteType class_byte_type = MBIM_UICC_CLASS_BYTE_TYPE_EXTENDED;
std::copy(kMbimEidReqApdu.begin(), kMbimEidReqApdu.end(), eid_apduCmd);
message = (mbim_message_ms_uicc_low_level_access_apdu_set_new(
channel_, secure_messaging, class_byte_type, kMbimEidReqApduSize,
eid_apduCmd, NULL));
mbim_device_command(device_.get(), message, kMbimResponseTimeout,
/*cancellable*/ NULL,
(GAsyncReadyCallback)UiccLowLevelAccessApduEidParse,
this);
}
void ModemMbim::TransmitMbimSendApdu(TxElement* tx_element) {
g_autoptr(MbimMessage) message = NULL;
MbimUiccSecureMessaging secure_messaging = MBIM_UICC_SECURE_MESSAGING_NONE;
MbimUiccClassByteType class_byte_type = MBIM_UICC_CLASS_BYTE_TYPE_EXTENDED;
uint8_t* fragment;
size_t apdu_len = 0;
uint8_t apduCmd[kMaxApduLen] = {0};
VLOG(2) << __func__;
ApduTxInfo* apdu = static_cast<ApduTxInfo*>(tx_element->info_.get());
size_t fragment_size = apdu->apdu_.GetNextFragment(&fragment);
VLOG(2) << "Fragment size" << fragment_size;
apdu_len = fragment_size;
std::copy(fragment, fragment + fragment_size, apduCmd);
apduCmd[apdu_len++] = 0x00;
VLOG(2) << "APDU fragment #"
<< " bytes): " << base::HexEncode(apduCmd, apdu_len);
VLOG(2) << "Send the apdu over channel number: " << channel_;
pending_response_ = true;
message = (mbim_message_ms_uicc_low_level_access_apdu_set_new(
channel_, secure_messaging, class_byte_type, apdu_len, apduCmd, NULL));
mbim_device_command(device_.get(), message, kMbimResponseTimeout,
/* cancellable */ NULL,
(GAsyncReadyCallback)UiccLowLevelAccessApduResponseParse,
this);
return;
}
void ModemMbim::QueryCurrentMbimCapabilities(ResultCallback cb) {
auto reacquire_channel = base::BindOnce(&ModemMbim::GetEidStepCloseChannel,
weak_factory_.GetWeakPtr());
tx_queue_.push_front(
{std::make_unique<TxInfo>(), AllocateId(),
std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimDeviceCaps),
base::BindOnce(&ModemMbim::RunNextStepOrRetry,
weak_factory_.GetWeakPtr(), std::move(reacquire_channel),
std::move(cb))});
TransmitFromQueue();
}
void ModemMbim::AcquireChannel(base::OnceCallback<void(int)> cb) {
LOG(INFO) << __func__;
tx_queue_.push_back(
{std::unique_ptr<TxInfo>(), AllocateId(),
std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimOpenLogicalChannel),
std::move(cb)});
TransmitFromQueue();
}
void ModemMbim::ReacquireChannel(const uint32_t physical_slot,
ResultCallback cb) {
LOG(INFO) << __func__ << " with physical_slot: " << physical_slot;
auto acquire_channel =
base::BindOnce(&ModemMbim::AcquireChannel, weak_factory_.GetWeakPtr());
tx_queue_.push_back(
{std::unique_ptr<TxInfo>(), AllocateId(),
std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimCloseLogicalChannel),
base::BindOnce(&RunNextStep, std::move(acquire_channel),
std::move(cb))});
TransmitFromQueue();
}
void ModemMbim::GetEidFromSim(ResultCallback cb) {
VLOG(2) << __func__;
tx_queue_.push_back(
{std::unique_ptr<TxInfo>(), AllocateId(),
std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimSendEidApdu),
std::move(cb)});
TransmitFromQueue();
}
void ModemMbim::GetEidStepCloseChannel(ResultCallback cb) {
auto open_channel = base::BindOnce(&ModemMbim::GetEidStepOpenChannel,
weak_factory_.GetWeakPtr());
tx_queue_.push_back(
{std::unique_ptr<TxInfo>(), AllocateId(),
std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimCloseLogicalChannel),
base::BindOnce(&RunNextStep, std::move(open_channel), std::move(cb))});
TransmitFromQueue();
}
void ModemMbim::GetEidStepOpenChannel(ResultCallback cb) {
VLOG(2) << __func__;
auto get_eid_from_sim =
base::BindOnce(&ModemMbim::GetEidFromSim, weak_factory_.GetWeakPtr());
tx_queue_.push_back(
{std::unique_ptr<TxInfo>(), AllocateId(),
std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimOpenLogicalChannel),
base::BindOnce(&ModemMbim::RunNextStepOrRetry,
weak_factory_.GetWeakPtr(), std::move(get_eid_from_sim),
std::move(cb))});
TransmitFromQueue();
}
/* static */
void ModemMbim::SubscriberReadyStatusRspCb(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(MbimMessage) response = NULL;
VLOG(2) << __func__;
g_autoptr(GError) error = NULL;
MbimSubscriberReadyState ready_state =
MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED;
g_autofree gchar* subscriber_id = NULL;
response = mbim_device_command_finish(device, res, &error);
if (response &&
mbim_message_response_get_result(response, MBIM_MESSAGE_TYPE_COMMAND_DONE,
&error) &&
mbim_message_subscriber_ready_status_response_parse(
response, &ready_state, &subscriber_id,
/* sim_iccid */ NULL,
/* ready_info */ NULL,
/* telephone_numbers_count */ NULL,
/* telephone_numbers */ NULL, &error)) {
modem_mbim->ready_state_ = ready_state;
LOG(INFO) << "Current Sim status: " << ready_state;
if (ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) {
VLOG(2) << "Sim not inserted";
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
if (ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED)
VLOG(2) << "Profile already enabled";
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
LOG(ERROR) << __func__ << "Failed due to error: " << error->message;
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
}
/* static */
void ModemMbim::DeviceCapsQueryReady(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(MbimMessage) response = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar* caps_device_id = NULL;
VLOG(2) << __func__;
response = mbim_device_command_finish(device, res, &error);
if (!response ||
!mbim_message_response_get_result(
response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
!mbim_message_device_caps_response_parse(response, NULL, /* device_type */
NULL, /* cellular class */
NULL, /* voice_class */
NULL, /* sim_class */
NULL, /* data_class */
NULL, /* sms_caps */
NULL, /* ctrl_caps */
NULL, /* max_sessions */
NULL, /* custom_data_class */
&caps_device_id,
NULL, /* firmware_info */
NULL, /* hardware_info */
&error)) {
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
modem_mbim->imei_ = caps_device_id;
VLOG(2) << "IMEI received from modem: " << modem_mbim->imei_;
modem_mbim->ProcessMbimResult(kModemSuccess);
}
/* static */
void ModemMbim::UiccLowLevelAccessCloseChannelSetCb(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(GError) error = NULL;
g_autoptr(MbimMessage) response = NULL;
guint32 status;
LOG(INFO) << __func__;
response = mbim_device_command_finish(device, res, &error);
modem_mbim->pending_response_ = false;
if (response &&
mbim_message_response_get_result(response, MBIM_MESSAGE_TYPE_COMMAND_DONE,
&error) &&
mbim_message_ms_uicc_low_level_access_close_channel_response_parse(
response, &status, &error)) {
modem_mbim->channel_ = kInvalidChannel;
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
if (g_error_matches(error, MBIM_STATUS_ERROR,
MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED)) {
LOG(INFO) << "Operation not allowed from modem: " << error->message;
} else {
LOG(INFO) << "Channel could not be closed: " << error->message;
}
modem_mbim->ProcessMbimResult(kModemSuccess);
}
/* static */
void ModemMbim::UiccLowLevelAccessOpenChannelSetCb(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(GError) error = NULL;
g_autoptr(MbimMessage) response = NULL;
guint32 status;
guint32 chl;
guint32 rsp_size;
const guint8* rsp = NULL;
LOG(INFO) << __func__;
response = mbim_device_command_finish(device, res, &error);
modem_mbim->pending_response_ = false;
if (response &&
mbim_message_response_get_result(response, MBIM_MESSAGE_TYPE_COMMAND_DONE,
&error) &&
mbim_message_ms_uicc_low_level_access_open_channel_response_parse(
response, &status, &chl, &rsp_size, &rsp, &error)) {
if (status != kMbimMessageSuccess) {
LOG(ERROR) << "Failed to open channel: " << error->message;
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
VLOG(2) << "Successfully opened channel: " << chl;
modem_mbim->channel_ = chl;
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
if (g_error_matches(error, MBIM_STATUS_ERROR,
MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED)) {
LOG(ERROR) << "Operation not allowed from modem: " << error->message;
} else {
LOG(ERROR) << "Failed to open channel" << error->message;
}
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
}
/* static */
void ModemMbim::UiccLowLevelAccessApduEidParse(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(GError) error = NULL;
g_autoptr(MbimMessage) response = NULL;
guint32 status;
guint32 response_size = 0;
const guint8* out_response = NULL;
std::vector<uint8_t> kGetEidDgiTag = {0xBF, 0x3E, 0x12, 0x5A, 0x10};
response = mbim_device_command_finish(device, res, &error);
if (response &&
mbim_message_response_get_result(response, MBIM_MESSAGE_TYPE_COMMAND_DONE,
&error) &&
mbim_message_ms_uicc_low_level_access_apdu_response_parse(
response, &status, &response_size, &out_response, &error)) {
if (response_size < 2 || out_response[0] != kGetEidDgiTag[0] ||
out_response[1] != kGetEidDgiTag[1]) {
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
VLOG(2) << "Adding to payload from APDU response (" << response_size
<< " bytes)"
<< base::HexEncode(&out_response[kGetEidDgiTag.size()],
response_size - kGetEidDgiTag.size());
for (int j = kGetEidDgiTag.size(); j < response_size; j++) {
modem_mbim->eid_ += bcd_chars[(out_response[j] >> 4) & 0xF];
modem_mbim->eid_ += bcd_chars[out_response[j] & 0xF];
}
LOG(INFO) << "EID for physical slot: " << modem_mbim->logical_slot_ + 1
<< " is " << modem_mbim->eid_;
if (modem_mbim->current_state_ == State::kMbimInitializeStarted)
modem_mbim->current_state_.Transition(State::kMbimStarted);
modem_mbim->euicc_manager_->OnEuiccUpdated(
modem_mbim->logical_slot_ + 1,
EuiccSlotInfo(modem_mbim->logical_slot_, std::move(modem_mbim->eid_)));
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
LOG(INFO) << "Could not find eSIM";
modem_mbim->euicc_manager_->OnEuiccRemoved(modem_mbim->logical_slot_ + 1);
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
/* static */
void ModemMbim::UiccLowLevelAccessApduResponseParse(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(GError) error = NULL;
g_autoptr(MbimMessage) response = NULL;
guint32 status;
guint32 response_size = 0;
const guint8* out_response;
CHECK(modem_mbim->tx_queue_.size());
// Ensure that the queued element is for a kSendApdu command
TxInfo* base_info = modem_mbim->tx_queue_[0].info_.get();
CHECK(base_info);
static ResponseApdu payload;
ApduTxInfo* info = static_cast<ApduTxInfo*>(base_info);
response = mbim_device_command_finish(device, res, &error);
modem_mbim->pending_response_ = false;
if (response &&
mbim_message_response_get_result(response, MBIM_MESSAGE_TYPE_COMMAND_DONE,
&error) &&
mbim_message_ms_uicc_low_level_access_apdu_response_parse(
response, &status, &response_size, &out_response, &error)) {
VLOG(2) << "Adding to payload from APDU response (" << response_size
<< " bytes): " << base::HexEncode(out_response, response_size);
payload.AddData(out_response, response_size);
if (payload.MorePayloadIncoming()) {
// Make the next transmit operation be a request for more APDU data
info->apdu_ = payload.CreateGetMoreCommand(/*is_extended_apdu*/ false);
return;
}
if (info->apdu_.HasMoreFragments()) {
// Send next fragment of APDU
VLOG(2) << "Sending next APDU fragment";
modem_mbim->TransmitFromQueue();
return;
}
// In case of mbim there are no appended status bytes in the APDU received.
// Hence no extra bytes to be removed.
modem_mbim->responses_.push_back(payload.ReleaseOnly());
std::move(modem_mbim->tx_queue_[0].cb_).Run(lpa::card::EuiccCard::kNoError);
modem_mbim->tx_queue_.pop_front();
modem_mbim->TransmitFromQueue();
} else {
std::move(modem_mbim->tx_queue_[0].cb_)
.Run(lpa::card::EuiccCard::kSendApduError);
modem_mbim->tx_queue_.pop_front();
modem_mbim->TransmitFromQueue();
return;
}
}
/* static */
void ModemMbim::ClientIndicationCb(MbimDevice* device,
MbimMessage* notification,
ModemMbim* modem_mbim) {
MbimService service;
g_autoptr(GError) error = NULL;
service = mbim_message_indicate_status_get_service(notification);
VLOG(2) << "Received notification for service: "
<< mbim_service_get_string(service);
VLOG(2) << "Command received from the modem: "
<< mbim_cid_get_printable(
service, mbim_message_indicate_status_get_cid(notification));
switch (service) {
case MBIM_SERVICE_BASIC_CONNECT:
if (mbim_message_indicate_status_get_cid(notification) ==
MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS) {
MbimSubscriberReadyState ready_state;
if (mbim_message_subscriber_ready_status_notification_parse(
notification, &ready_state,
/* subscriber_id */ NULL,
/* sim_iccid */ NULL,
/* ready_info */ NULL,
/* telephone_numbers_count */ NULL,
/* telephone_numbers */ NULL, &error)) {
modem_mbim->ready_state_ = ready_state;
modem_mbim->is_ready_state_valid = true;
LOG(INFO) << "Current sim status: " << ready_state;
if (ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED) {
VLOG(2) << "Sim has one profile enabled";
} else if (ready_state ==
MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) {
VLOG(2) << "Sim not inserted";
}
}
}
break;
default:
VLOG(2) << "Indication received is not handled";
break;
}
return;
}
bool ModemMbim::State::Transition(ModemMbim::State::Value value) {
bool valid_transition = true;
switch (value) {
case kMbimUninitialized:
valid_transition = true;
break;
default:
// Most states can only transition from the previous state.
valid_transition = (value == value_ + 1);
}
if (valid_transition) {
LOG(INFO) << "Transitioning from state " << *this << " to state "
<< State(value);
value_ = value;
} else {
LOG(ERROR) << "Cannot transition from state " << *this << " to state "
<< State(value);
}
return valid_transition;
}
void ModemMbim::StoreAndSetActiveSlot(const uint32_t physical_slot,
ResultCallback cb) {
// Get the slot from libmbim
LOG(INFO) << __func__ << " physical_slot:" << physical_slot;
auto reacquire_channel = base::BindOnce(
&ModemMbim::ReacquireChannel, weak_factory_.GetWeakPtr(), physical_slot);
// The modem may be reset, causing device_ to be invalid. Retry init to be
// safe.
RetryInitialization(base::BindOnce(&RunNextStep, std::move(reacquire_channel),
std::move(cb)));
}
void ModemMbim::StartProfileOp(uint32_t physical_slot, ResultCallback cb) {
LOG(INFO) << __func__ << " physical_slot:" << physical_slot;
retry_count_ = 0;
is_ready_state_valid = false;
StoreAndSetActiveSlot(physical_slot, std::move(cb));
}
void ModemMbim::FinishProfileOp(ResultCallback cb) {
LOG(INFO) << __func__;
const guint MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE = 7;
if (!is_ready_state_valid ||
!(ready_state_ == MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED ||
ready_state_ == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED ||
ready_state_ == MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE)) {
if (retry_count_ > kMaxRetries) {
LOG(ERROR) << "Could not finish profile operation, ready_state_="
<< ready_state_
<< ", is_ready_state_valid=" << is_ready_state_valid;
std::move(cb).Run(kModemMessageProcessingError);
return;
}
retry_count_++;
executor_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ModemMbim::FinishProfileOp, weak_factory_.GetWeakPtr(),
std::move(cb)),
kSimRefreshDelay);
return;
}
retry_count_ = 0;
AcquireChannel(std::move(cb));
}
void ModemMbim::RestoreActiveSlot(ResultCallback cb) {
LOG(INFO) << __func__ << "++";
std::move(cb).Run(kModemSuccess);
}
bool ModemMbim::IsSimValidAfterEnable() {
VLOG(2) << __func__ << "++";
return false;
// The sim issues a proactive refresh after an enable. This
// function should return true immediately after the refresh completes,
// However, the LPA expects that this function does not read any
// other state variable. Thus, we simply return false until the LPA
// times out, and then finish the operation. This imposes a 15 sec penalty
// on every enable and 30 sec penalty on every disable.
// A workaround is to return true and complete the eSIM operation before the
// refresh. FinishProfileOp can gate the dbus response until the refresh is
// complete. However, this exposes UI issues.
}
bool ModemMbim::IsSimValidAfterDisable() {
VLOG(2) << __func__ << "++";
return false;
}
} // namespace hermes