blob: c60aca06f559450141b84530e10e645b03288892 [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/hermes_common.h"
#include "hermes/logger.h"
#include "hermes/modem.h"
#include "hermes/socket_qrtr.h"
#include "hermes/uim_cmd.h"
namespace hermes {
// Implementation of EuiccCard using QRTR sockets to send QMI UIM
// messages.
class ModemQrtr : public Modem<QmiCmdInterface> {
static std::unique_ptr<ModemQrtr> Create(
std::unique_ptr<SocketInterface> socket,
Logger* logger,
Executor* executor);
virtual ~ModemQrtr();
// EuiccInterface overrides
void Initialize(EuiccManagerInterface* euicc_manager,
ResultCallback cb) override;
// ModemControlInterface overrides
void StoreAndSetActiveSlot(uint32_t physical_slot,
ResultCallback cb) override;
void RestoreActiveSlot(ResultCallback cb) override;
void StartProfileOp(uint32_t physical_slot, ResultCallback cb) override;
void FinishProfileOp(ResultCallback cb) override;
struct SwitchSlotTxInfo : public TxInfo {
explicit SwitchSlotTxInfo(const uint32_t physical_slot,
const uint8_t logical_slot)
: physical_slot_(physical_slot), logical_slot_(logical_slot) {}
const uint32_t physical_slot_;
const uint8_t logical_slot_;
// Delay between SwitchSlot and the next QMI message. Allows the modem to
// power on the new slot, and for the eUICC to boot. If this delay is
// insufficient, we retry after kInitRetryDelay
static constexpr auto kSwitchSlotDelay = base::TimeDelta::FromSeconds(3);
ModemQrtr(std::unique_ptr<SocketInterface> socket,
Logger* logger,
Executor* executor);
void InitializeUim();
void RetryInitialization(ResultCallback cb);
void Shutdown() override;
// Helper methods to create TxElements and add them to the queue.
void SendReset(ResultCallback cb);
void SendOpenLogicalChannel(base::OnceCallback<void(int)> cb);
// 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() override;
// 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);
std::unique_ptr<QmiCmdInterface> GetTagForSendApdu() override;
// 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.
int ReceiveQmiReset(const qrtr_packet& packet);
// Performs decoding for SWITCH_SLOT QMI response.
int ReceiveQmiSwitchSlot(const qrtr_packet& packet);
// Performs decoding for GET_SLOTS QMI response.
int ReceiveQmiGetSlots(const qrtr_packet& packet);
// Performs decoding for OPEN_LOGICAL_CHANNEL QMI response.
int ReceiveQmiOpenLogicalChannel(const qrtr_packet& packet);
int ParseQmiOpenLogicalChannel(const qrtr_packet& packet);
// Performs decoding for SEND_APDU response and calls |on_recv_| with
// appropriate parameters.
int ReceiveQmiSendApdu(const qrtr_packet& packet);
// Performs decoding of GET_DEVICE_SERIAL_NUMBERS response. Parses the IMEI.
int ReceiveQmiGetSerialNumbers(const qrtr_packet& packet);
void DisableQmi(base::TimeDelta duration);
void EnableQmi();
void OnDataAvailable(SocketInterface* socket);
// Set the active slot to a euicc so that a channel can be established and
// profiles can be installed.
void SetActiveSlot(const uint32_t physical_slot, ResultCallback cb);
// 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 AcquireChannel(base::OnceCallback<void(int)> cb);
friend class ModemQrtrTest;
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 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) {
switch (state.value_) {
case kUninitialized:
os << "Uninitialized";
case kInitializeStarted:
os << "InitializeStarted";
case kDmsStarted:
os << "DmsStarted";
case kUimStarted:
os << "UimStarted";
return os;
explicit State(Value value) : value_(value) {}
Value value_;
State current_state_;
bool qmi_disabled_;
base::OnceClosure retry_initialization_callback_;
int retry_count_;
ResultCallback init_done_cb_;
// 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_;
// 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_;
// Buffer for storing data from the QRTR socket
std::vector<uint8_t> buffer_;
std::map<std::pair<QmiCmdInterface::Service, uint16_t>,
base::Callback<int(const qrtr_packet&)>>
base::WeakPtrFactory<ModemQrtr> weak_factory_;
} // namespace hermes