Files
MengMengCode 22487dbb1f Add PC/SC backend support for USB SIM readers
- Implemented a new `unsupportedBackend` in `backend_stub.go` to handle unsupported platforms.
- Created a `Service` struct in `service.go` to manage interactions with smart cards, including session management and identity reading.
- Added methods for reading identity, checking readiness, and authenticating with USIM applications.
- Introduced a `scriptedCard` for testing purposes in `service_test.go` to simulate card responses.
- Defined necessary types and error handling in `types.go` for better clarity and usability.
- Developed a `PCSCAdapter` in `vowifi/pcsc_adapter.go` to integrate PC/SC service with Wi-Fi calling functionalities.
- Added a new SVG icon for USB SIM readers in `public/sim-reader.svg`.
- Implemented tests to ensure correct functionality and error handling in various scenarios.
2026-08-12 02:33:32 +08:00

243 lines
9.5 KiB
Go

package device
import (
"errors"
"time"
"vocat/internal/modem"
)
var (
ErrNotStarted = errors.New("device manager is not started")
ErrNotFound = errors.New("device not found")
ErrNoATPort = errors.New("device has no usable AT port")
ErrUnsupportedCapability = errors.New("device does not support this capability")
ErrSMSPromptUnsupported = errors.New("device AT client does not support SMS prompt mode")
ErrSMSInvalidRecipient = errors.New("invalid SMS recipient")
ErrSMSEmpty = errors.New("SMS text is empty")
ErrSMSTooLong = errors.New("SMS exceeds one-message encoding limit")
ErrSMSReferenceMissing = errors.New("modem completed SMS command without a message reference")
ErrSMSInvalidMessageIndex = errors.New("invalid SMS message index")
ErrDataBackendUnavailable = errors.New("cellular data backend is unavailable")
ErrInvalidNetworkAPN = errors.New("invalid cellular APN")
ErrRegionBlocked = errors.New("sim card home region is not served")
ErrUSSDSessionNotFound = errors.New("ussd session not found or already closed")
)
type NetworkRequest struct {
Enabled bool `json:"enabled"`
APN string `json:"apn"`
IPVersion string `json:"ipVersion"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Authentication string `json:"authentication,omitempty"`
Backend string `json:"backend,omitempty"`
}
type NetworkResult struct {
Enabled bool `json:"enabled"`
Backend string `json:"backend"`
Interface string `json:"interface,omitempty"`
ControlDevice string `json:"controlDevice,omitempty"`
APN string `json:"apn,omitempty"`
IPVersion string `json:"ipVersion,omitempty"`
Detail string `json:"detail,omitempty"`
}
type USBNetMode struct {
Mode int `json:"mode"`
Name string `json:"name"`
}
type OperatorSelection struct {
Mode int `json:"mode"`
Format int `json:"format"`
Operator string `json:"operator"`
AccessTechnology string `json:"accessTechnology,omitempty"`
}
type Device struct {
ID string `json:"id"`
Candidate modem.Candidate `json:"candidate"`
Snapshot *Snapshot `json:"snapshot,omitempty"`
LastError string `json:"lastError,omitempty"`
Discovered bool `json:"discovered"`
LastUpdated time.Time `json:"lastUpdated,omitempty"`
}
type PhoneNumber struct {
Number string `json:"number"`
Source string `json:"source"`
Status string `json:"status"`
}
const (
PhoneSourceCNUM = "at_cnum"
PhoneSourceOwnNumber = "sim_own_number"
PhoneSourceEFMSISDN = "usim_ef_msisdn"
)
type Snapshot struct {
DeviceID string `json:"deviceId"`
Port string `json:"port"`
Responsive bool `json:"responsive"`
Manufacturer string `json:"manufacturer"`
Model string `json:"model"`
Firmware string `json:"firmware"`
SIMStatus string `json:"simStatus"`
SIMReady bool `json:"simReady"`
SIMChanged bool `json:"simChanged,omitempty"`
SignalRaw *int `json:"signalRaw,omitempty"`
SignalPercent *int `json:"signalPercent,omitempty"`
RSSIDBm *int `json:"rssiDbm,omitempty"`
RSRP *int `json:"rsrp,omitempty"`
RSRQ *int `json:"rsrq,omitempty"`
SINR *int `json:"sinr,omitempty"`
AccessTech string `json:"accessTech"`
Band string `json:"band"`
Channel string `json:"channel"`
OperatorName string `json:"operatorName"`
OperatorCode string `json:"operatorCode"`
RegistrationStatus int `json:"registrationStatus"`
RegistrationSource string `json:"registrationSource"`
PSAttached bool `json:"psAttached"`
IMEI string `json:"imei"`
ICCID string `json:"iccid"`
IMSI string `json:"imsi"`
SPN string `json:"spn,omitempty"`
OperatingMode int `json:"operatingMode"`
ModeKnown bool `json:"modeKnown"`
FlightMode bool `json:"flightMode"`
RadioOff bool `json:"radioOff"`
Phone PhoneNumber `json:"phone"`
Warnings []string `json:"warnings,omitempty"`
UpdatedAt time.Time `json:"updatedAt"`
}
type USSDResult struct {
Code string `json:"code"`
Text string `json:"text"`
Raw string `json:"raw"`
DCS *int `json:"dcs,omitempty"`
// Status describes the dialog state: "final", "awaiting_input",
// "terminated", or "failed".
Status string `json:"status,omitempty"`
// SessionID identifies an open dialog when Status is "awaiting_input".
SessionID string `json:"sessionId,omitempty"`
// Continueable reports whether the network expects more input on SessionID.
Continueable bool `json:"continueable,omitempty"`
}
type FlightResult struct {
PreviousMode int `json:"previousMode"`
CurrentMode int `json:"currentMode"`
Changed bool `json:"changed"`
FlightMode bool `json:"flightMode"`
RadioOff bool `json:"radioOff"`
}
type SMSEncoding string
const (
SMSEncodingGSM7Text SMSEncoding = "gsm7_text"
SMSEncodingGSM7PDU SMSEncoding = "gsm7_pdu"
SMSEncodingUCS2PDU SMSEncoding = "ucs2_pdu"
SMSEncoding8BitPDU SMSEncoding = "8bit_pdu"
SMSEncodingUnknown SMSEncoding = "unknown"
)
type SMSStorageStatus string
const (
SMSStatusReceivedUnread SMSStorageStatus = "received_unread"
SMSStatusReceivedRead SMSStorageStatus = "received_read"
SMSStatusStoredUnsent SMSStorageStatus = "stored_unsent"
SMSStatusStoredSent SMSStorageStatus = "stored_sent"
SMSStatusUnknown SMSStorageStatus = "unknown"
)
type SMSDirection string
const (
SMSDirectionReceived SMSDirection = "received"
SMSDirectionSubmitted SMSDirection = "submitted"
SMSDirectionStatusReport SMSDirection = "status_report"
SMSDirectionUnknown SMSDirection = "unknown"
)
type SMSConcatInfo struct {
Reference int `json:"reference"`
Total int `json:"total"`
Sequence int `json:"sequence"`
}
// SMSSubmitTPDU is one modem-independent SMS-SUBMIT transfer unit. TPDU does
// not include the SMSC-length octet used by AT+CMGS PDU mode, so it can be
// embedded directly in an RP-DATA message for SMS over IMS.
type SMSSubmitTPDU struct {
To string
Encoding SMSEncoding
TPDU []byte
Part int
Total int
ConcatReference *int
}
type SMSMessage struct {
Index int `json:"index"`
Storage string `json:"storage,omitempty"`
StorageStatus SMSStorageStatus `json:"storageStatus"`
Direction SMSDirection `json:"direction"`
From string `json:"from,omitempty"`
To string `json:"to,omitempty"`
ServiceCenter string `json:"serviceCenter,omitempty"`
Text string `json:"text"`
Encoding SMSEncoding `json:"encoding"`
ServiceCenterTimestamp *time.Time `json:"serviceCenterTimestamp,omitempty"`
DischargeTimestamp *time.Time `json:"dischargeTimestamp,omitempty"`
MessageReference *int `json:"messageReference,omitempty"`
StatusCode *int `json:"statusCode,omitempty"`
DeliveryStatus string `json:"deliveryStatus,omitempty"`
Concat *SMSConcatInfo `json:"concat,omitempty"`
ProtocolID int `json:"protocolId"`
DataCodingScheme int `json:"dataCodingScheme"`
ModemLength int `json:"modemLength"`
RawPDU string `json:"rawPdu"`
RawUserData string `json:"rawUserData,omitempty"`
DecodeError string `json:"decodeError,omitempty"`
}
type SMSPartResult struct {
Part int `json:"part"`
Total int `json:"total"`
MessageReference int `json:"messageReference"`
ReferenceKnown bool `json:"referenceKnown"`
AcceptedByModem bool `json:"acceptedByModem"`
SubmissionStatus string `json:"submissionStatus"`
ModemFinal string `json:"modemFinal"`
ModemEvidence []string `json:"modemEvidence"`
SubmittedAt time.Time `json:"submittedAt"`
}
// SMSSendResult proves only that the modem accepted the submit request. A
// +CMGS message reference is not a network delivery receipt.
type SMSSendResult struct {
To string `json:"to"`
Encoding SMSEncoding `json:"encoding"`
MessageReference int `json:"messageReference"`
ReferenceKnown bool `json:"referenceKnown"`
AcceptedByModem bool `json:"acceptedByModem"`
DeliveryConfirmed bool `json:"deliveryConfirmed"`
SubmissionStatus string `json:"submissionStatus"`
DeliveryStatus string `json:"deliveryStatus"`
ModemFinal string `json:"modemFinal"`
ModemEvidence []string `json:"modemEvidence"`
SubmittedAt time.Time `json:"submittedAt"`
PartsTotal int `json:"partsTotal"`
PartsAttempted int `json:"partsAttempted"`
PartsAccepted int `json:"partsAccepted"`
AllPartsAccepted bool `json:"allPartsAccepted"`
ConcatReference *int `json:"concatReference,omitempty"`
PartResults []SMSPartResult `json:"partResults"`
}