blob: 0406bcf6d2c863f2f6e7842e506a87f5955842ed [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 <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 int kExecutorIndex = 0;
const guint kMbimTimeoutSeconds = 30;
constexpr int kMbimMessageSuccess = 144;
constexpr auto kSlotInfoDelay = base::Seconds(5);
// 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,
};
// ModemManager uses channel_group=1. Make Hermes use 2 just to be cautious.
constexpr int kChannelGroupId = 2;
std::vector<uint8_t> AidIsdr() {
return {hermes::kAidIsdr.begin(), hermes::kAidIsdr.end()};
}
bool GetReadyState(MbimDevice* device,
bool is_notification,
MbimMessage* notification,
MbimSubscriberReadyState* ready_state) {
g_autoptr(GError) error = NULL;
if (mbim_device_check_ms_mbimex_version(device, 3, 0)) {
MbimSubscriberReadyStatusFlag flags =
MBIM_SUBSCRIBER_READY_STATUS_FLAG_NONE;
auto parser_v3 =
is_notification
? &mbim_message_ms_basic_connect_v3_subscriber_ready_status_notification_parse
: &mbim_message_ms_basic_connect_v3_subscriber_ready_status_response_parse;
if (!parser_v3(notification, ready_state, &flags, NULL, NULL,
NULL, /* ready_info */
NULL, /* telephone_numbers_count */
NULL, /* telephone_numbers */
&error)) {
LOG(ERROR) << __func__ << ": Failed due to error: " << error->message;
return false;
}
} else {
auto parser = is_notification
? &mbim_message_subscriber_ready_status_notification_parse
: &mbim_message_subscriber_ready_status_response_parse;
if (!parser(notification, ready_state,
/* subscriber_id */ NULL,
/* sim_iccid */ NULL,
/* ready_info */ NULL,
/* telephone_numbers_count */ NULL,
/* telephone_numbers */ NULL, &error)) {
LOG(ERROR) << __func__ << ": Failed due to error: " << error->message;
return false;
}
}
return true;
}
} // namespace
namespace hermes {
/* static */
std::unique_ptr<ModemMbim> ModemMbim::Create(
Logger* logger,
Executor* executor,
std::unique_ptr<ModemManagerProxy> modem_manager_proxy) {
VLOG(2) << __func__;
return std::unique_ptr<ModemMbim>(
new ModemMbim(logger, executor, std::move(modem_manager_proxy)));
}
ModemMbim::ModemMbim(Logger* logger,
Executor* executor,
std::unique_ptr<ModemManagerProxy> modem_manager_proxy)
: Modem<MbimCmd>(logger, executor, std::move(modem_manager_proxy)),
channel_(kInvalidChannel),
is_ready_state_valid(false),
ready_state_(MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED),
slot_info_(),
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;
InitializationStep(State::kMbimUninitialized, std::move(cb));
}
void ModemMbim::Shutdown() {
LOG(INFO) << __func__;
CloseDevice();
channel_ = kInvalidChannel;
ready_state_ = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED;
current_state_.Transition(State::kMbimUninitialized);
slot_info_.Clear();
eid_read_failed_ = false;
modem_manager_proxy_->ScheduleUninhibit(base::Seconds(0));
}
void ModemMbim::TransmitFromQueue() {
VLOG(2) << __func__;
if (tx_queue_.empty() || retry_initialization_callback_) {
return;
}
if (!device_) {
LOG(ERROR) << "No MBIM device. Cannot transmit MBIM message";
ProcessMbimResult(kModemMessageProcessingError);
return;
}
auto mbim_cmd = tx_queue_[0].msg_.get();
switch (mbim_cmd->mbim_type()) {
case MbimCmd::MbimType::kMbimOpenChannel:
TransmitOpenChannel();
break;
case MbimCmd::MbimType::kMbimCloseChannel:
TransmitCloseChannel();
break;
case MbimCmd::MbimType::kMbimSendApdu:
TransmitMbimSendApdu(&tx_queue_[0]);
break;
case MbimCmd::MbimType::kMbimSubscriberStatusReady:
TransmitSubscriberStatusReady();
break;
case MbimCmd::MbimType::kMbimDeviceCaps:
TransmitDeviceCaps();
break;
case MbimCmd::MbimType::kMbimSendEidApdu:
TransmitSendEidApdu();
break;
case MbimCmd::MbimType::kMbimSlotInfoStatus:
TransmitSlotInfoStatus();
break;
case MbimCmd::MbimType::kMbimDeviceSlotMapping:
TransmitDeviceSlotMapping();
break;
case MbimCmd::MbimType::kMbimSetDeviceSlotMapping:
TransmitSetDeviceSlotMapping();
break;
case MbimCmd::MbimType::kMbimSysCaps:
TransmitSysCapsQuery();
break;
default:
VLOG(2) << "no instruction";
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__;
CHECK(modem_mbim) << "modem_mbim does not exist";
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(INFO) << "The modem may be booting ...";
modem_mbim->RetryInitialization(std::move(modem_mbim->init_done_cb_));
return;
}
mbim_device_open_full(modem_mbim->device_.get(), MBIM_DEVICE_OPEN_FLAGS_PROXY,
kMbimTimeoutSeconds, /* 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;
base::OnceCallback<void(base::OnceCallback<void(int)>)> get_caps;
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);
if (modem_mbim->current_state_ == State::kMbimStarted) {
VLOG(2) << "Opened device. Reusing previous IMEI";
std::move(modem_mbim->init_done_cb_).Run(kModemSuccess);
return;
}
LOG(INFO) << "Mbim device is ready, acquire imei and eid";
modem_mbim->InitializationStep(State::kReadImei,
std::move(modem_mbim->init_done_cb_));
}
void ModemMbim::InitializationStep(ModemMbim::State::Value next_state,
ResultCallback cb) {
// cb is executed only after all initialization steps complete or if there is
// an error. cb is passed in from Initialize and passed around until the last
// step. At the last step, the cb is executed.
current_state_.Transition(next_state);
switch (next_state) {
case State::Value::kMbimUninitialized:
// Store cb in init_done_cb_ so that MbimCreateNewDeviceCb can use it
// while calling InitializationStep(kGetSubscriberReadyState)
init_done_cb_ = std::move(cb);
modem_manager_proxy_->WaitForModem(base::BindOnce(
&ModemMbim::InitializationStep, weak_factory_.GetWeakPtr(),
State::kMbimInitializeStarted, std::move(cb)));
break;
case State::kMbimInitializeStarted: {
if (modem_manager_proxy_->GetPrimaryPort().empty()) {
LOG(ERROR) << __func__ << ": Could not get primary port from MM";
std::move(init_done_cb_).Run(kModemManagerError);
break;
}
std::string dev_path = "/dev/" + modem_manager_proxy_->GetPrimaryPort();
const gchar* const path = dev_path.c_str();
LOG(INFO) << __func__ << ": Opening path:" << path;
file_ = g_file_new_for_path(path);
mbim_device_new(file_, /* cancellable */ NULL,
(GAsyncReadyCallback)MbimCreateNewDeviceCb,
this); // MbimCreateNewDeviceCb will call
// InitializationStep(kReadImei)
break;
}
case State::kReadImei:
SendMessage(MbimCmd::MbimType::kMbimDeviceCaps,
std::make_unique<TxInfo>(), std::move(cb),
&ModemMbim::InitializationStep,
State::kGetSubscriberReadyState);
break;
case State::kGetSubscriberReadyState:
SendMessage(MbimCmd::MbimType::kMbimSubscriberStatusReady,
std::unique_ptr<TxInfo>(), std::move(cb),
&ModemMbim::InitializationStep, State::kCheckSingleSim);
break;
case State::kCheckSingleSim: {
if (!mbim_device_check_ms_mbimex_version(device_.get(), 2, 0)) {
// we have a single sim device. Skip to reading EID if a SIM was seen.
if (ready_state_ == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) {
VLOG(2) << "Sim not inserted";
// skip retries, close device, listen for a new modem, and run cb
retry_count_ = kMaxRetries + 1;
RetryInitialization(std::move(cb));
break;
}
slot_info_.InitSlotInfo(1 /* slot_count */, 1 /* map_count */);
slot_info_.cached_active_slot_ = 0;
slot_info_.slot_state_[0] =
MBIM_UICC_SLOT_STATE_ACTIVE_ESIM; // Assume we have an eSIM until
// we can confirm the EID. This
// is an mbimv1 limitation.
InitializationStep(State::kCloseChannel, std::move(cb));
break;
}
InitializationStep(State::kSysQuery, std::move(cb));
break;
}
case State::kSysQuery:
SendMessage(MbimCmd::MbimType::kMbimSysCaps, std::unique_ptr<TxInfo>(),
std::move(cb), &ModemMbim::InitializationStep,
State::kDeviceSlotMapping);
break;
case State::kDeviceSlotMapping:
// To figure out active slot
SendMessage(MbimCmd::MbimType::kMbimDeviceSlotMapping,
std::make_unique<SwitchSlotTxInfo>(0), std::move(cb),
&ModemMbim::InitializationStep, State::kSlotInfo);
break;
case State::kSlotInfo:
ReadSlotsInfo(
0 /* slot_num */, 0 /* retry_count */,
std::move(
cb)); // ReadSlotsInfo calls InitializationStep(CloseChannel)
// after slot info for all slots has been read. This is an
// exception to the rule that all state transitions must be
// captured in InitializationStep.
break;
case State::kCloseChannel:
if (!slot_info_.IsEsimActive()) {
InitializationStep(State::kMbimStarted, std::move(cb));
break;
}
SendMessage(MbimCmd::MbimType::kMbimCloseChannel,
std::unique_ptr<TxInfo>(), std::move(cb),
&ModemMbim::InitializationStep, State::kOpenChannel);
break;
case State::kOpenChannel:
SendMessage(MbimCmd::MbimType::kMbimOpenChannel,
std::make_unique<OpenChannelTxInfo>(AidIsdr()), std::move(cb),
&ModemMbim::InitializationStep, State::kReadEid);
break;
case State::kReadEid:
SendMessage(MbimCmd::MbimType::kMbimSendEidApdu,
std::unique_ptr<TxInfo>(), std::move(cb),
&ModemMbim::InitializationStep, State::kEidReadComplete);
break;
case State::kEidReadComplete:
SendMessage(MbimCmd::MbimType::kMbimCloseChannel,
std::unique_ptr<TxInfo>(), std::move(cb),
&ModemMbim::InitializationStep, State::kMbimStarted);
break;
case State::kMbimStarted:
CloseDevice();
if (eid_read_failed_) {
LOG(ERROR) << "EID read failed";
eid_read_failed_ = false;
std::move(cb).Run(kModemMessageProcessingError);
return;
}
VLOG(2) << "eSIM initialized for MBIM modem";
std::move(cb).Run(kModemSuccess);
OnEuiccUpdated();
break;
}
}
void ModemMbim::OnEuiccUpdated() {
VLOG(2) << __func__ << ": " << slot_info_;
for (int i = 0; i < slot_info_.slot_count_; i++) {
if (slot_info_.IsEuicc(i)) {
auto euicc_slot_info =
(slot_info_.cached_active_slot_ == i)
? EuiccSlotInfo(kExecutorIndex, slot_info_.eid_[i])
: EuiccSlotInfo(slot_info_.eid_[i]);
euicc_manager_->OnEuiccUpdated(i + 1, EuiccSlotInfo(slot_info_.eid_[i]));
}
}
euicc_manager_->OnLogicalSlotUpdated(slot_info_.cached_active_slot_ + 1,
slot_info_.cached_active_slot_ + 1);
}
void ModemMbim::ReadSlotsInfo(uint32_t slot_num,
uint32_t retry_count,
ResultCallback cb) {
VLOG(2) << __func__ << ": slot_num:" << slot_num;
if (slot_num == slot_info_.slot_count_) {
if (slot_info_.IsEuiccPresentOnAnySlot() || retry_count >= kMaxRetries) {
InitializationStep(State::kCloseChannel, std::move(cb));
return;
}
// post a delayed task after 5secs
executor_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ModemMbim::ReadSlotsInfo, weak_factory_.GetWeakPtr(), 0,
++retry_count, std::move(cb)),
kSlotInfoDelay);
}
SendMessage(MbimCmd::MbimType::kMbimSlotInfoStatus,
std::make_unique<SwitchSlotTxInfo>(slot_num), std::move(cb),
&ModemMbim::ReadSlotsInfo, slot_num + 1, 0 /* retry_count */);
}
void ModemMbim::TransmitSysCapsQuery() {
g_autoptr(MbimMessage) message = NULL;
VLOG(2) << __func__;
message = mbim_message_ms_basic_connect_extensions_sys_caps_query_new(NULL);
if (!message) {
LOG(ERROR) << "Mbim message creation failed";
ProcessMbimResult(kModemMessageProcessingError);
return;
}
mbim_device_command(device_.get(), message, kMbimTimeoutSeconds,
/* cancellable */ NULL,
(GAsyncReadyCallback)QuerySysCapsReady, this);
}
void ModemMbim::TransmitSubscriberStatusReady() {
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, kMbimTimeoutSeconds,
/* cancellable */ NULL,
(GAsyncReadyCallback)SubscriberReadyStatusRspCb, this);
}
void ModemMbim::TransmitDeviceCaps() {
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, kMbimTimeoutSeconds,
/*cancellable*/ NULL,
(GAsyncReadyCallback)DeviceCapsQueryReady, this);
}
void ModemMbim::TransmitCloseChannel() {
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 */ 0, kChannelGroupId, &error);
if (!message) {
LOG(ERROR) << "Mbim message creation failed:" << error->message;
ProcessMbimResult(kModemMessageProcessingError);
return;
}
mbim_device_command(device_.get(), message, kMbimTimeoutSeconds,
/* cancellable */ NULL,
(GAsyncReadyCallback)UiccLowLevelAccessCloseChannelSetCb,
this);
}
void ModemMbim::TransmitOpenChannel() {
VLOG(2) << __func__;
auto open_channel_tx_info =
dynamic_cast<OpenChannelTxInfo*>(tx_queue_[0].info_.get());
guint8 appId[16];
guint32 appIdSize = open_channel_tx_info->aid_.size();
g_autoptr(GError) error = NULL;
g_autoptr(MbimMessage) message = NULL;
std::copy(open_channel_tx_info->aid_.begin(),
open_channel_tx_info->aid_.end(), appId);
message = mbim_message_ms_uicc_low_level_access_open_channel_set_new(
appIdSize, appId, /* selectP2arg */ 4, kChannelGroupId, &error);
if (!message) {
LOG(ERROR) << __func__ << ": Mbim Message Creation Failed";
ProcessMbimResult(kModemMessageProcessingError);
return;
}
mbim_device_command(device_.get(), message, kMbimTimeoutSeconds,
/*cancellable*/ NULL,
(GAsyncReadyCallback)UiccLowLevelAccessOpenChannelSetCb,
this);
}
void ModemMbim::TransmitSendEidApdu() {
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, kMbimTimeoutSeconds,
/* cancellable */ NULL,
(GAsyncReadyCallback)UiccLowLevelAccessApduEidParse,
this);
}
void ModemMbim::TransmitSlotInfoStatus() {
g_autoptr(MbimMessage) message = NULL;
g_autoptr(GError) error = NULL;
auto switch_slot_tx_info =
dynamic_cast<SwitchSlotTxInfo*>(tx_queue_[0].info_.get());
VLOG(2) << __func__ << ": slot:" << switch_slot_tx_info->physical_slot_;
message =
(mbim_message_ms_basic_connect_extensions_slot_info_status_query_new(
switch_slot_tx_info->physical_slot_, &error));
if (!message) {
LOG(ERROR) << "Mbim message creation failed";
return;
}
mbim_device_command(device_.get(), message, kMbimTimeoutSeconds,
/* cancellable */ NULL,
(GAsyncReadyCallback)DeviceSlotStatusInfoRspCb, this);
}
void ModemMbim::TransmitDeviceSlotMapping() {
g_autoptr(MbimMessage) message = NULL;
VLOG(2) << __func__;
message =
mbim_message_ms_basic_connect_extensions_device_slot_mappings_query_new(
NULL);
if (!message) {
LOG(ERROR) << "Mbim message creation failed";
return;
}
mbim_device_command(device_.get(), message, kMbimTimeoutSeconds,
/* cancellable */ NULL,
(GAsyncReadyCallback)DeviceSlotStatusMappingRspCb, this);
}
void ModemMbim::TransmitSetDeviceSlotMapping() {
g_autoptr(MbimMessage) message = NULL;
g_autoptr(MbimSlotArray) slot_mappings =
NULL; // Array of MbimSlotArray. Every executor gets a MbimSlotArray.
// MbimSlotArray always holds a single element.
slot_mappings = g_new0(MbimSlot*, slot_info_.map_count_ + 1);
slot_mappings[kExecutorIndex] = g_new0(MbimSlot, 1);
VLOG(2) << __func__;
auto switch_slot_tx_info =
dynamic_cast<SwitchSlotTxInfo*>(tx_queue_[0].info_.get());
VLOG(2) << __func__ << ": Hermes trying to operate on slot:"
<< switch_slot_tx_info->physical_slot_;
slot_mappings[kExecutorIndex]->slot = switch_slot_tx_info->physical_slot_;
message =
mbim_message_ms_basic_connect_extensions_device_slot_mappings_set_new(
slot_info_.map_count_, (const MbimSlot**)slot_mappings, NULL);
if (!message) {
LOG(ERROR) << "Mbim message creation failed";
return;
}
LOG(INFO) << __func__ << ": before mbim_device_command";
mbim_device_command(device_.get(), message, kMbimTimeoutSeconds,
/* cancellable */ NULL,
(GAsyncReadyCallback)SetDeviceSlotMappingsRspCb, 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; // append extra byte for 4G Mbim Modem required for apdu handling
LOG(INFO) << "Sending APDU fragment (" << apdu_len << " bytes): over channel "
<< channel_;
VLOG(2) << "APDU:" << base::HexEncode(apduCmd, apdu_len);
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, kMbimTimeoutSeconds,
/* cancellable */ NULL,
(GAsyncReadyCallback)UiccLowLevelAccessApduResponseParse,
this);
return;
}
void ModemMbim::ReacquireChannel(EuiccEventStep step,
std::vector<uint8_t> aid,
ResultCallback cb) {
LOG(INFO) << __func__ << ":" << to_underlying(step);
switch (step) {
case EuiccEventStep::CLOSE_CHANNEL:
SendMessage(MbimCmd::MbimType::kMbimCloseChannel,
std::unique_ptr<TxInfo>(), std::move(cb),
&ModemMbim::ReacquireChannel, EuiccEventStep::OPEN_CHANNEL,
std::move(aid));
break;
case EuiccEventStep::OPEN_CHANNEL:
SendMessage(MbimCmd::MbimType::kMbimOpenChannel,
std::make_unique<OpenChannelTxInfo>(std::move(aid)),
std::move(cb), &ModemMbim::ReacquireChannel,
EuiccEventStep::EUICC_EVENT_STEP_LAST, std::move(aid));
break;
case EuiccEventStep::EUICC_EVENT_STEP_LAST:
std::move(cb).Run(kModemSuccess);
break;
default:
VLOG(2) << "No Suitable operation";
}
}
/* static */
void ModemMbim::SubscriberReadyStatusRspCb(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(MbimMessage) response = NULL;
VLOG(2) << __func__;
g_autoptr(GError) error = NULL;
response = mbim_device_command_finish(device, res, &error);
if (!GetReadyState(device, false /* is_notification */, response,
&modem_mbim->ready_state_)) {
LOG(ERROR) << "Could not parse ready state";
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
LOG(INFO) << "Current Sim status:" << modem_mbim->ready_state_;
modem_mbim->ProcessMbimResult(kModemSuccess);
}
/* 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);
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);
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(INFO) << "Could not open channel:" << error->message
<< ". Inserted sim may not be an eSIM.";
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
VLOG(2) << "Successfully opened channel:" << chl;
modem_mbim->channel_ = chl;
modem_mbim->open_channel_raw_response_.clear();
for (int i = 0; i < rsp_size; i++)
modem_mbim->open_channel_raw_response_.push_back(rsp[i]);
modem_mbim->open_channel_raw_response_.push_back(status & 0xFF);
modem_mbim->open_channel_raw_response_.push_back((status >> 8) & 0xFF);
VLOG(2) << __func__ << " Open Channel Response: "
<< base::HexEncode(modem_mbim->open_channel_raw_response_.data(),
modem_mbim->open_channel_raw_response_.size());
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
if (g_error_matches(error, MBIM_STATUS_ERROR,
MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED)) {
LOG(INFO) << "Modem FW may not support eSIM:" << error->message;
} else {
LOG(INFO) << "Could not open channel:" << error->message
<< ". Inserted sim may not be an eSIM.";
}
// not being able to open a channel is irrecoverable on L850. On dual sim
// modems too, we should not attempt to open a channel unless we know we have
// an eSIM.
modem_mbim->retry_count_ = kMaxRetries + 1;
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
}
/* static */
bool ModemMbim::ParseEidApduResponse(const MbimMessage* response,
std::string* eid,
ModemMbim* modem_mbim) {
g_autoptr(GError) error = NULL;
guint32 status;
guint32 response_size = 0;
const guint8* out_response = NULL;
std::vector<uint8_t> kGetEidDgiTag = {0xBF, 0x3E, 0x12, 0x5A, 0x10};
if (!response)
return false;
if (!mbim_message_response_get_result(
response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
LOG(ERROR) << "Could not parse EID: " << error->message;
if (modem_mbim) {
// We've likely encountered b/230851574. Set flag that closes the channel,
// and restores the slot.
modem_mbim->retry_count_ = kMaxRetries + 1;
modem_mbim->eid_read_failed_ = true;
}
return false;
}
if (!mbim_message_ms_uicc_low_level_access_apdu_response_parse(
response, &status, &response_size, &out_response, &error)) {
LOG(ERROR) << "Could not parse EID: " << error->message;
return false;
}
if (response_size < 2 || out_response[0] != kGetEidDgiTag[0] ||
out_response[1] != kGetEidDgiTag[1]) {
return false;
}
VLOG(2) << "Decoding EID 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++) {
*eid += bcd_chars[(out_response[j] >> 4) & 0xF];
*eid += bcd_chars[out_response[j] & 0xF];
}
return true;
}
/* static */
void ModemMbim::UiccLowLevelAccessApduEidParse(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(GError) error = NULL;
std::string eid;
g_autoptr(MbimMessage) response = NULL;
response = mbim_device_command_finish(device, res, &error);
if (ParseEidApduResponse(response, &eid, modem_mbim)) {
VLOG(2) << "EID for physical slot:"
<< modem_mbim->slot_info_.cached_active_slot_ << " is " << eid;
modem_mbim->slot_info_.SetSlotStateActiveSlot(MBIM_UICC_SLOT_STATE_UNKNOWN);
if (!eid.empty()) {
modem_mbim->slot_info_.SetEidActiveSlot(std::move(eid));
modem_mbim->slot_info_.SetSlotStateActiveSlot(
MBIM_UICC_SLOT_STATE_ACTIVE_ESIM);
}
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
LOG(ERROR) << "Could not read EID";
// An EID read failure should usually abort any eSIM operation. In the special
// case where we encounter b/230851574, eid_read_failed_ is set. In such
// cases, do not abort the eSIM operation immediately. Instead, declare
// success, and expect any subsequent code to read the eid_read_failed_ flag
// and close any channels and reverse any slot switches.
if (modem_mbim->eid_read_failed_) {
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
}
/* 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);
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)) {
LOG(INFO) << "Adding to payload from APDU response (" << response_size
<< " bytes)";
VLOG(2) << "Payload: " << base::HexEncode(out_response, response_size)
<< ", status: " << status;
payload.AddData(out_response, response_size);
payload.AddStatusBytes((status >> 8) & 0xFF, status & 0xFF);
if (payload.MorePayloadIncoming()) {
// Make the next transmit operation be a request for more APDU data
info->apdu_ = payload.CreateGetMoreCommand(/* is_extended_apdu */ false,
info->apdu_.cls_);
LOG(INFO) << "Requesting more APDUs...";
modem_mbim->TransmitFromQueue();
return;
}
if (info->apdu_.HasMoreFragments()) {
// Send next fragment of APDU
LOG(INFO) << "Sending next APDU fragment...";
modem_mbim->TransmitFromQueue();
return;
}
modem_mbim->responses_.push_back(std::move(payload));
std::move(modem_mbim->tx_queue_[0].cb_).Run(lpa::card::EuiccCard::kNoError);
modem_mbim->tx_queue_.pop_front();
modem_mbim->TransmitFromQueue();
} else {
LOG(ERROR) << __func__ << ": Failed to parse APDU response";
std::move(modem_mbim->tx_queue_[0].cb_)
.Run(lpa::card::EuiccCard::kSendApduError);
modem_mbim->tx_queue_.pop_front();
modem_mbim->TransmitFromQueue();
return;
}
}
void ModemMbim::DeviceSlotStatusInfoRspCb(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(MbimMessage) response = NULL;
g_autoptr(GError) error = NULL;
guint32 slot_index;
MbimUiccSlotState slot_status;
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_basic_connect_extensions_slot_info_status_response_parse(
response, &slot_index, &slot_status, &error)) {
modem_mbim->slot_info_.slot_state_[slot_index] = slot_status;
VLOG(2) << "Response received with slot_index:" << slot_index
<< " & status:" << slot_status;
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
LOG(ERROR) << __func__ << ":" << error->message;
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
}
void ModemMbim::QuerySysCapsReady(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(MbimMessage) response = NULL;
g_autoptr(GError) error = NULL;
guint32 number_executors;
guint32 number_slots;
guint32 concurrency;
guint64 modem_id;
LOG(INFO) << __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_ms_basic_connect_extensions_sys_caps_response_parse(
response, &number_executors, &number_slots, &concurrency, &modem_id,
&error)) {
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
VLOG(2) << "executor index:" << number_executors
<< " Number of slots:" << number_slots;
modem_mbim->slot_info_.InitSlotInfo(number_slots, number_executors);
modem_mbim->ProcessMbimResult(kModemSuccess);
}
void ModemMbim::DeviceSlotStatusMappingRspCb(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(MbimMessage) response = NULL;
g_autoptr(GError) error = NULL;
guint32 map_count = 0;
g_autoptr(MbimSlotArray) slot_mappings = 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_ms_basic_connect_extensions_device_slot_mappings_response_parse(
response, &map_count, &slot_mappings, &error)) {
CHECK_EQ(map_count, 1) << "Unexpected multi radio modem";
modem_mbim->slot_info_.map_count_ = map_count;
modem_mbim->slot_info_.cached_active_slot_ =
slot_mappings[kExecutorIndex]->slot;
modem_mbim->slot_info_.get_slot_mapping_result_ =
slot_mappings[kExecutorIndex]->slot;
VLOG(2) << "Map count:" << map_count
<< "& current active slot:" << slot_mappings[kExecutorIndex]->slot;
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
}
void ModemMbim::SetDeviceSlotMappingsRspCb(MbimDevice* device,
GAsyncResult* res,
ModemMbim* modem_mbim) {
g_autoptr(MbimMessage) response = NULL;
g_autoptr(GError) error = NULL;
guint32 map_count = 0;
uint8_t physical_switch_slot = 1;
g_autoptr(MbimSlotArray) slot_mappings = NULL;
VLOG(2) << __func__;
auto switch_slot_tx_info =
dynamic_cast<SwitchSlotTxInfo*>(modem_mbim->tx_queue_[0].info_.get());
physical_switch_slot = switch_slot_tx_info->physical_slot_;
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_basic_connect_extensions_device_slot_mappings_response_parse(
response, &map_count, &slot_mappings, &error)) {
LOG(ERROR) << "Sim slot switch to " << physical_switch_slot << " failed";
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
if (physical_switch_slot != slot_mappings[kExecutorIndex]->slot) {
LOG(ERROR) << "Sim slot switch to " << physical_switch_slot << " failed";
// b/230851574 causes a slot switch to fail once inevitably.
if (modem_mbim->eid_read_failed_) {
LOG(INFO) << "Ignoring failed slot switch";
modem_mbim->ProcessMbimResult(kModemSuccess);
return;
}
modem_mbim->ProcessMbimResult(kModemMessageProcessingError);
return;
}
modem_mbim->slot_info_.cached_active_slot_ = physical_switch_slot;
for (int i = 0; i < modem_mbim->slot_info_.slot_count_; i++) {
if (i != physical_switch_slot) {
modem_mbim->euicc_manager_->OnLogicalSlotUpdated(
modem_mbim->slot_info_.cached_active_slot_, std::nullopt);
continue;
}
modem_mbim->euicc_manager_->OnLogicalSlotUpdated(
modem_mbim->slot_info_.cached_active_slot_, kExecutorIndex);
}
LOG(INFO) << "Sim switch was successful to:"
<< slot_mappings[kExecutorIndex]->slot;
modem_mbim->ProcessMbimResult(kModemSuccess);
}
/* static */
void ModemMbim::ClientIndicationCb(MbimDevice* device,
MbimMessage* notification,
ModemMbim* modem_mbim) {
MbimService service;
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) {
modem_mbim->is_ready_state_valid =
GetReadyState(device, true /*is_notification*/, notification,
&modem_mbim->ready_state_);
LOG(INFO) << "Current sim status:" << modem_mbim->ready_state_;
if (modem_mbim->ready_state_ ==
MBIM_SUBSCRIBER_READY_STATE_INITIALIZED) {
VLOG(2) << "Sim has one profile enabled";
} else if (modem_mbim->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;
}
void ModemMbim::CloseDevice() {
if (device_ && g_signal_handler_is_connected(device_.get(), indication_id_))
g_signal_handler_disconnect(device_.get(), indication_id_);
device_.reset();
}
bool ModemMbim::State::Transition(ModemMbim::State::Value value) {
bool valid_transition;
switch (value) {
case kMbimUninitialized:
valid_transition = true;
break;
case kCloseChannel:
valid_transition = (value_ == kCheckSingleSim || value_ == kSlotInfo);
break;
case kMbimStarted:
valid_transition =
(value_ == kCloseChannel || value_ == kEidReadComplete);
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;
}
std::ostream& operator<<(std::ostream& os, const ModemMbim::State state) {
switch (state.value_) {
case ModemMbim::State::kMbimUninitialized:
os << "Uninitialized";
break;
case ModemMbim::State::kMbimInitializeStarted:
os << "InitializeStarted";
break;
case ModemMbim::State::kReadImei:
os << "GetReadImei";
break;
case ModemMbim::State::kGetSubscriberReadyState:
os << "GetSubscriberReadyState";
break;
case ModemMbim::State::kCheckSingleSim:
os << "CheckSingleSim";
break;
case ModemMbim::State::kSysQuery:
os << "GetSysQuery";
break;
case ModemMbim::State::kDeviceSlotMapping:
os << "GetDeviceSlotMapping";
break;
case ModemMbim::State::kSlotInfo:
os << "GetSlotInfo";
break;
case ModemMbim::State::kCloseChannel:
os << "Close Channel";
break;
case ModemMbim::State::kOpenChannel:
os << "Open Channel";
break;
case ModemMbim::State::kReadEid:
os << "GetReadEid";
break;
case ModemMbim::State::kEidReadComplete:
os << "kEidReadComplete";
break;
case ModemMbim::State::kMbimStarted:
os << "MbimStarted";
break;
}
return os;
}
void ModemMbim::OnEuiccEventStart(const uint32_t physical_slot,
bool switch_slot_only,
EuiccEventStep step,
ResultCallback cb) {
LOG(INFO) << __func__ << ": slot=" << physical_slot
<< " switch_slot_only=" << switch_slot_only
<< " step=" << to_underlying(step);
switch (step) {
case EuiccEventStep::GET_MBIM_DEVICE:
CloseDevice();
{
auto next_step = (switch_slot_only) ? EuiccEventStep::CLOSE_CHANNEL
: EuiccEventStep::GET_SLOT_MAPPING;
auto get_slot_mapping = base::BindOnce(
&ModemMbim::OnEuiccEventStart, weak_factory_.GetWeakPtr(),
physical_slot, switch_slot_only, next_step);
init_done_cb_ = base::BindOnce(
&RunNextStep, std::move(get_slot_mapping), std::move(cb));
mbim_device_new(file_, /* cancellable */ NULL,
(GAsyncReadyCallback)MbimCreateNewDeviceCb, this);
}
break;
case EuiccEventStep::GET_SLOT_MAPPING:
if (!mbim_device_check_ms_mbimex_version(device_.get(), 2, 0)) {
OnEuiccEventStart(physical_slot, false /* switch_slot_only */
,
EuiccEventStep::CLOSE_CHANNEL, std::move(cb));
break;
}
SendMessage(MbimCmd::MbimType::kMbimDeviceSlotMapping,
std::make_unique<SwitchSlotTxInfo>(physical_slot),
std::move(cb), &ModemMbim::OnEuiccEventStart, physical_slot,
switch_slot_only, EuiccEventStep::GET_SLOT_INFO);
break;
case EuiccEventStep::GET_SLOT_INFO: {
SendMessage(MbimCmd::MbimType::kMbimSlotInfoStatus,
std::make_unique<SwitchSlotTxInfo>(physical_slot),
std::move(cb), &ModemMbim::OnEuiccEventStart, physical_slot,
switch_slot_only, EuiccEventStep::CLOSE_CHANNEL);
} break;
case EuiccEventStep::CLOSE_CHANNEL: {
// b/230851574 Channel must be closed before attempting a slot switch.
SendMessage(MbimCmd::MbimType::kMbimCloseChannel,
std::unique_ptr<TxInfo>(), std::move(cb),
&ModemMbim::OnEuiccEventStart, physical_slot,
switch_slot_only, EuiccEventStep::SET_SLOT_MAPPING);
} break;
case EuiccEventStep::SET_SLOT_MAPPING: {
auto next_step = (switch_slot_only)
? EuiccEventStep::EUICC_EVENT_STEP_LAST
: EuiccEventStep::OPEN_CHANNEL;
// Bypass slot switch if we are already on the requested slot or if slot
// switch isn't supported
if (physical_slot == slot_info_.cached_active_slot_ ||
!mbim_device_check_ms_mbimex_version(device_.get(), 2, 0)) {
OnEuiccEventStart(physical_slot, switch_slot_only, next_step,
std::move(cb));
break;
}
SendMessage(MbimCmd::MbimType::kMbimSetDeviceSlotMapping,
std::make_unique<SwitchSlotTxInfo>(physical_slot),
std::move(cb), &ModemMbim::OnEuiccEventStart, physical_slot,
switch_slot_only, next_step);
} break;
case EuiccEventStep::OPEN_CHANNEL:
if (!slot_info_.IsEsimActive()) {
LOG(ERROR) << "Active slot does not have an eSIM";
euicc_manager_->OnEuiccRemoved(slot_info_.cached_active_slot_ + 1);
std::move(cb).Run(kModemMessageProcessingError);
break;
}
SendMessage(MbimCmd::MbimType::kMbimOpenChannel,
std::make_unique<OpenChannelTxInfo>(AidIsdr()), std::move(cb),
&ModemMbim::OnEuiccEventStart, physical_slot,
switch_slot_only, EuiccEventStep::GET_EID);
break;
case EuiccEventStep::GET_EID:
SendMessage(MbimCmd::MbimType::kMbimSendEidApdu,
std::unique_ptr<TxInfo>(), std::move(cb),
&ModemMbim::OnEuiccEventStart, physical_slot,
switch_slot_only, EuiccEventStep::CHECK_EID);
break;
case EuiccEventStep::CHECK_EID:
if (eid_read_failed_) {
OnEidReadFailed(physical_slot, EidReadFailedStep::CLOSE_CHANNEL,
std::move(cb));
break;
}
OnEuiccEventStart(physical_slot, switch_slot_only,
EuiccEventStep::EUICC_EVENT_STEP_LAST, std::move(cb));
break;
case EuiccEventStep::EUICC_EVENT_STEP_LAST:
OnEuiccUpdated();
std::move(cb).Run(kModemSuccess);
break;
}
}
void ModemMbim::OnEidReadFailed(const uint32_t physical_slot,
EidReadFailedStep step,
ResultCallback cb) {
// b/230851574 An EID error may have occurred after a slot switch.
// Attempt to switch back to the original slot after closing the
// channel, while still reporting an error.
LOG(INFO) << __func__ << ": slot=" << physical_slot
<< " step=" << to_underlying(step);
switch (step) {
case EidReadFailedStep::CLOSE_CHANNEL:
SendMessage(MbimCmd::MbimType::kMbimCloseChannel,
std::unique_ptr<TxInfo>(), std::move(cb),
&ModemMbim::OnEidReadFailed, physical_slot,
EidReadFailedStep::RESTORE_SLOT_ATTEMPT1);
break;
case EidReadFailedStep::RESTORE_SLOT_ATTEMPT1:
if (!slot_info_.get_slot_mapping_result_.has_value()) {
LOG(ERROR) << "Cannot restore slot after EID read failure";
eid_read_failed_ = false;
std::move(cb).Run(kModemMessageProcessingError);
break;
}
// This attempt will most likely fail due to b/230851574
SendMessage(MbimCmd::MbimType::kMbimSetDeviceSlotMapping,
std::make_unique<SwitchSlotTxInfo>(
slot_info_.get_slot_mapping_result_.value()),
std::move(cb), &ModemMbim::OnEidReadFailed, physical_slot,
EidReadFailedStep::RESTORE_SLOT_ATTEMPT2);
break;
case EidReadFailedStep::RESTORE_SLOT_ATTEMPT2:
SendMessage(MbimCmd::MbimType::kMbimSetDeviceSlotMapping,
std::make_unique<SwitchSlotTxInfo>(
slot_info_.get_slot_mapping_result_.value()),
std::move(cb), &ModemMbim::OnEidReadFailed, physical_slot,
EidReadFailedStep::STEP_LAST);
break;
case EidReadFailedStep::STEP_LAST:
eid_read_failed_ = false;
std::move(cb).Run(kModemMessageProcessingError);
break;
}
}
void ModemMbim::ProcessEuiccEvent(EuiccEvent event, ResultCallback cb) {
LOG(INFO) << __func__ << ": " << event;
if (event.step == EuiccStep::START) {
auto on_euicc_event_start = base::BindOnce(
&ModemMbim::OnEuiccEventStart, weak_factory_.GetWeakPtr(),
event.slot - 1, false /* switch_slot_only */,
EuiccEventStep::GET_MBIM_DEVICE);
modem_manager_proxy_->WaitForModemAndInhibit(base::BindOnce(
&RunNextStep, std::move(on_euicc_event_start), std::move(cb)));
return;
}
if (event.step == EuiccStep::PENDING_NOTIFICATIONS) {
AcquireChannelAfterCardReady(event, std::move(cb));
return;
}
if (event.step == EuiccStep::END) {
slot_info_.get_slot_mapping_result_.reset();
auto close_device_and_uninhibit = base::BindOnce(
&ModemMbim::CloseDeviceAndUninhibit, weak_factory_.GetWeakPtr());
// Close channel, followed by close device and uninhibit, and then execute
// cb
SendMessage(MbimCmd::MbimType::kMbimCloseChannel, std::unique_ptr<TxInfo>(),
std::move(cb), &ModemMbim::CloseDeviceAndUninhibit);
return;
}
}
void ModemMbim::AcquireChannelAfterCardReady(EuiccEvent event,
ResultCallback cb) {
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_DEVICE_LOCKED ||
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::AcquireChannelAfterCardReady,
weak_factory_.GetWeakPtr(), std::move(event),
std::move(cb)),
kSimRefreshDelay);
return;
}
retry_count_ = 0;
ReacquireChannel(EuiccEventStep::OPEN_CHANNEL, AidIsdr(), std::move(cb));
}
void ModemMbim::CloseDeviceAndUninhibit(ResultCallback cb) {
CloseDevice();
modem_manager_proxy_->ScheduleUninhibit(kUninhibitDelay);
std::move(cb).Run(kModemSuccess);
}
void ModemMbim::RestoreActiveSlot(ResultCallback cb) {
LOG(INFO) << __func__;
if (!mbim_device_check_ms_mbimex_version(device_.get(), 2, 0)) {
std::move(cb).Run(kModemSuccess);
return;
}
if (!slot_info_.get_slot_mapping_result_.has_value()) {
LOG(ERROR) << "Could not find slot number to switch to";
std::move(cb).Run(kModemMessageProcessingError);
return;
}
if (slot_info_.get_slot_mapping_result_ == slot_info_.cached_active_slot_) {
VLOG(2) << __func__ << "Already on the right slot";
std::move(cb).Run(kModemSuccess);
return;
}
OnEuiccEventStart(slot_info_.get_slot_mapping_result_.value(),
true /* switch_slot_only */,
EuiccEventStep::GET_MBIM_DEVICE, std::move(cb));
}
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;
}
void ModemMbim::OpenConnection(
const std::vector<uint8_t>& aid,
base::OnceCallback<void(std::vector<uint8_t>)> cb) {
LOG(INFO) << __func__ << base::HexEncode(aid.data(), aid.size());
ReacquireChannel(EuiccEventStep::CLOSE_CHANNEL, aid,
base::BindOnce(&ModemMbim::OpenConnectionResponse,
weak_factory_.GetWeakPtr(), std::move(cb)));
}
void ModemMbim::TransmitApdu(
const std::vector<uint8_t>& apduCommand,
base::OnceCallback<void(std::vector<uint8_t>)> cb) {
LOG(INFO) << __func__ << ": APDU command="
<< base::HexEncode(apduCommand.data(), apduCommand.size());
DCHECK(apduCommand.size() > 2) << "APDU does not have a header.";
CommandApdu apdu(apduCommand);
auto transmit_apdu_resp =
base::BindOnce(&ModemMbim::TransmitApduResponse,
weak_factory_.GetWeakPtr(), std::move(cb));
tx_queue_.push_back({std::make_unique<ApduTxInfo>(std::move(apdu)),
AllocateId(), GetTagForSendApdu(),
std::move(transmit_apdu_resp)});
TransmitFromQueue();
}
/* static */
bool ModemMbim::ParseEidApduResponseForTesting(const MbimMessage* response,
std::string* eid) {
return ParseEidApduResponse(response, eid, nullptr);
}
template <typename Func, typename... Args>
void ModemMbim::SendMessage(MbimCmd::MbimType type,
std::unique_ptr<TxInfo> tx_info,
ResultCallback cb,
Func&& next_step,
Args&&... args) {
// First, transmit an mbim message. If the message is successful, run
// next_step(args).
auto next_step_weak =
base::BindOnce(next_step, weak_factory_.GetWeakPtr(), args...);
auto run_next_step =
base::BindOnce(&ModemMbim::RunNextStepOrRetry, weak_factory_.GetWeakPtr(),
std::move(next_step_weak), std::move(cb));
tx_queue_.push_back({std::move(tx_info), AllocateId(),
std::make_unique<MbimCmd>(type),
std::move(run_next_step)});
TransmitFromQueue();
}
bool ModemMbim::SlotInfo::IsEuiccPresentOnAnySlot() const {
for (int i = 0; i < slot_count_; i++) {
if (IsEuicc(i)) {
return true;
}
}
return false;
}
bool ModemMbim::SlotInfo::IsEuicc(int slot) const {
return (slot_state_[slot] == MBIM_UICC_SLOT_STATE_ACTIVE_ESIM) ||
(slot_state_[slot] == MBIM_UICC_SLOT_STATE_ACTIVE_ESIM_NO_PROFILES);
}
void ModemMbim::SlotInfo::InitSlotInfo(guint32 slot_count, guint32 map_count) {
slot_count_ = slot_count;
map_count_ = map_count; // number of executors/radios
slot_state_.clear();
eid_.clear();
for (int i = 0; i < slot_count_; i++) {
slot_state_.push_back(MBIM_UICC_SLOT_STATE_UNKNOWN);
eid_.emplace_back();
}
}
void ModemMbim::SlotInfo::SetEidActiveSlot(std::string eid) {
eid_[cached_active_slot_] = std::move(eid);
}
void ModemMbim::SlotInfo::SetSlotStateActiveSlot(MbimUiccSlotState state) {
slot_state_[cached_active_slot_] = state;
}
std::ostream& operator<<(std::ostream& os, const ModemMbim::SlotInfo& info) {
os << "map_count_: " << info.map_count_
<< " slot_count_: " << info.slot_count_
<< " cached_active_slot_: " << info.cached_active_slot_;
for (int i = 0; i < info.slot_count_; i++) {
os << " slot_state[" << i << "]: " << info.slot_state_[i] << " eid[" << i
<< "]:" << info.eid_[i];
}
return os;
}
} // namespace hermes