blob: 02e4518499b546bda2cd3ae92a4122cac5cb0bcf [file] [log] [blame]
// Copyright 2018 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 <deque>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <google-lpa/lpa/card/euicc_card.h>
#include <libqrtr.h>
#include "hermes/dms_cmd.h"
#include "hermes/executor.h"
#include "hermes/logger.h"
#include "hermes/modem_control_interface.h"
#include "hermes/socket_qrtr.h"
#include "hermes/uim_cmd.h"
namespace hermes {
class EuiccManagerInterface;
// Implementation of EuiccCard using QRTR sockets to send QMI UIM
// messages.
class ModemQrtr : public lpa::card::EuiccCard, public ModemControlInterface {
// Base class for the tx info specific to a certain type of uim command.
// Uim command types that need any additional information should define
// a child class. An instance of that class should be set to the data pointer
// in its corresponding TxElement.
struct TxInfo {
virtual ~TxInfo() = default;
using ResponseCallback =
responses, // NOLINT(runtime/references)
int err)>;
static std::unique_ptr<ModemQrtr> Create(
std::unique_ptr<SocketInterface> socket,
Logger* logger,
Executor* executor);
virtual ~ModemQrtr();
void Initialize(EuiccManagerInterface* euicc_manager);
// ModemControlInterface overrides
void StoreAndSetActiveSlot(uint32_t physical_slot) override;
void RestoreActiveSlot() override;
void StartProfileOp(uint32_t physical_slot) override;
void FinishProfileOp() override;
// lpa::card::EuiccCard overrides.
void SendApdus(std::vector<lpa::card::Apdu> apdus,
ResponseCallback cb) override;
bool IsSimValidAfterEnable() override;
bool IsSimValidAfterDisable() override;
std::string GetImei() override { return imei_; };
lpa::util::EuiccLog* logger() override { return logger_; }
// Delay between SwitchSlot and the next QMI message
static constexpr auto kSwitchSlotDelay = base::TimeDelta::FromSeconds(1);
struct TxElement {
std::unique_ptr<TxInfo> info_;
uint16_t id_;
std::unique_ptr<QmiCmdInterface> qmi_msg_;
ModemQrtr(std::unique_ptr<SocketInterface> socket,
Logger* logger,
Executor* executor);
void InitializeUim();
void RetryInitialization();
void FinalizeInitialization();
void Shutdown();
uint16_t AllocateId();
// Top-level method to transmit an element from the tx queue. Dispatches to
// the proper Transmit*CmdFromQueue method based on the service being
// transmitted to.
void TransmitFromQueue();
// Transmit*CmdFromQueue methods perform QMI encoding prior to sending
// data to the socket. Will remove elements from the tx queue as needed.
void TransmitDmsCmdFromQueue();
void TransmitUimCmdFromQueue();
// Creates and sends a SWITCH_SLOT QMI request
void TransmitQmiSwitchSlot(TxElement* tx_element);
// Creates and sends OPEN_LOGICAL_CHANNEL QMI request.
void TransmitQmiOpenLogicalChannel(TxElement* tx_element);
// Creates and sends SEND_APDU QMI request.
void TransmitQmiSendApdu(TxElement* tx_element);
// Performs QMI encoding and sends data to the QRTR socket.
bool SendCommand(QmiCmdInterface* qmi_command,
uint16_t id,
void* c_struct,
qmi_elem_info* ei);
// Top-level method when a packet is read from the socket into |buffer_|. Will
// perform proper processing based on QRTR packet type. Attempts to transmit
// the next element in the tx queue when complete.
void ProcessQrtrPacket(uint32_t node, uint32_t port, int size);
// Dispatches to proper ReceiveQmi* method based on QMI type.
void ProcessQmiPacket(const qrtr_packet& packet);
// Performs decoding for UIM RESET QMI response.
void ReceiveQmiReset(const qrtr_packet& packet);
// Performs decoding for SWITCH_SLOT QMI response.
void ReceiveQmiSwitchSlot(const qrtr_packet& packet);
// Performs decoding for GET_SLOTS QMI response.
void ReceiveQmiGetSlots(const qrtr_packet& packet);
// Performs decoding for OPEN_LOGICAL_CHANNEL QMI response.
void ReceiveQmiOpenLogicalChannel(const qrtr_packet& packet);
void ParseQmiOpenLogicalChannel(const qrtr_packet& packet);
// Performs decoding for SEND_APDU response and calls |on_recv_| with
// appropriate parameters.
void ReceiveQmiSendApdu(const qrtr_packet& packet);
// Performs decoding of GET_DEVICE_SERIAL_NUMBERS response. Parses the IMEI.
void ReceiveQmiGetSerialNumbers(const qrtr_packet& packet);
void DisableQmi(base::TimeDelta duration);
void EnableQmi();
void OnDataAvailable(SocketInterface* socket);
// lpa::card::EuiccCard overrides.
const lpa::proto::EuiccSpecVersion& GetCardVersion() override;
lpa::util::Executor* executor() override { return executor_; }
// Set the active slot to a euicc so that a channel can be established and
// profiles can be installed.
void SetActiveSlot(uint32_t physical_slot);
// Request that the Euicc does not send intermediate procedure bytes.
// Useful in eliminating race between card refresh and profile enable response
// b/169954635
enum class ProcedureBytesMode : uint8_t {
EnableIntermediateBytes = 0,
DisableIntermediateBytes = 1
void SetProcedureBytes(ProcedureBytesMode procedure_bytes_mode);
void ReacquireChannel();
friend class ModemQrtrTest;
// State Diagram //
// [Start state]
// +---------------+ (FinalizeInitialization() called w/failure)
// | Uninitialized | <--------------------------------------------------+
// +---------------+ |
// + |
// | (Initialize() called) |
// | |
// V |
// +-------------------+ +------------+ +------------+ |
// | InitializeStarted | +-> | DmsStarted | +-> | UimStarted | +---+ |
// +-------------------+ +------------+ +------------+ | |
// | |
// +-----------------------------------------------------+ |
// | |
// V |
// +-----------------------+ +----------------------+ |
// | LogicalChannelPending | +-> | LogicalChannelOpened | +---------------+
// +-----------------------+ +----------------------+ |
// |
// +------------------------------------------------------------+
// | (FinalizeInitialization() called w/success)
// V
// +---------------+
// | SendApduReady |
// +---------------+
class State {
enum Value : uint8_t {
State() : value_(kUninitialized) {}
// Transitions to the indicated state. Returns whether or not the transition
// was successful.
bool Transition(Value value);
bool IsInitialized() const { return value_ == kSendApduReady; }
// Returns whether or not some QMI packet can be sent out in the state. Note
// that APDUs in particular may only be sent in the kSendApduReady state.
bool CanSend() const {
return value_ == kDmsStarted || value_ == kUimStarted ||
value_ == kSendApduReady;
bool operator==(Value value) const { return value_ == value; }
bool operator!=(Value value) const { return value_ != value; }
friend std::ostream& operator<<(std::ostream& os, const State state) {
os << state.value_;
return os;
explicit State(Value value) : value_(value) {}
Value value_;
State current_state_;
bool qmi_disabled_;
// Indicates that a qmi message has been sent and that a response is expected
// Set for all known message types except QMI_RESET
std::unique_ptr<QmiCmdInterface> pending_response_type_;
bool extended_apdu_supported_;
uint16_t current_transaction_id_;
// Logical Channel that will be used to communicate with the chip, returned
// from OPEN_LOGICAL_CHANNEL request sent once the QRTR socket has been
// opened.
uint8_t channel_;
// The slot that the logical channel to the eSIM will be made. Initialized in
// constructor, hardware specific.
uint8_t logical_slot_;
// Store the previous active slot before a switch slot
base::Optional<uint32_t> stored_active_slot_;
// Ask SendApdu commands to send final result and status words only.
// If set, intermediate procedure bytes are not sent by the Euicc.
ProcedureBytesMode procedure_bytes_mode_;
std::unique_ptr<SocketInterface> socket_;
// A bimap of {node,port} <-> Service .
// Stores information similar to output of qrtr-lookup
class QrtrTable {
std::unordered_map<QmiCmdInterface::Service, SocketQrtr::PacketMetadata>
std::unordered_map<SocketQrtr::PacketMetadata, QmiCmdInterface::Service>
bool ContainsService(QmiCmdInterface::Service service);
void Insert(QmiCmdInterface::Service service,
SocketQrtr::PacketMetadata metadata);
void clear();
const SocketQrtr::PacketMetadata& GetMetadata(
QmiCmdInterface::Service service);
const QmiCmdInterface::Service& GetService(
SocketQrtr::PacketMetadata metadata);
QrtrTable qrtr_table_;
std::string imei_;
// Buffer for storing data from the QRTR socket
std::vector<uint8_t> buffer_;
// List of responses for the oldest SendApdus call that hasn't been completely
// processed.
std::vector<std::vector<uint8_t>> responses_;
// Queue of packets to send to the modem
std::deque<TxElement> tx_queue_;
std::map<std::pair<QmiCmdInterface::Service, uint16_t>,
base::Callback<void(const qrtr_packet&)>>
// Used to send notifications about eSIM slot changes.
EuiccManagerInterface* euicc_manager_;
Logger* logger_;
Executor* executor_;
lpa::proto::EuiccSpecVersion spec_version_;
} // namespace hermes