Fix SIM2 QMI UIM provisioning, auto-enable ModemManager, PIN auto-prompt, call audio, and Dunst clearing

This commit is contained in:
Luxferre
2026-10-25 11:31:41 +02:00
parent 9b712dcbca
commit b56f310f21
41 changed files with 1193 additions and 352 deletions
+190 -26
View File
@@ -62,12 +62,15 @@ struct SimSlotInfo {
bool needsPin() const {
return state == "present" &&
(pin1State == "enabled-not-verified" || appState == "pin1");
(pin1State == "enabled-not-verified" ||
appState.rfind("pin1", 0) == 0);
}
bool needsPuk() const {
return state == "present" &&
(pin1State == "blocked" || appState == "puk1");
(pin1State == "blocked" ||
appState.rfind("puk1", 0) == 0 ||
appState == "pin1-blocked");
}
bool isPinEnabled() const {
@@ -88,6 +91,13 @@ struct ModemStatusData {
std::string mmFailedReason;
std::string mmPowerState = "off";
std::string mmUnlockRequired;
std::string mmRegState;
std::string nasRegState;
std::string nasOperator;
std::string nasMcc;
std::string nasMnc;
std::string nasRoaming;
std::string nasRadio;
int mmPinRetries = -1;
int mmPukRetries = -1;
std::string operatorName;
@@ -103,6 +113,56 @@ struct ModemStatusData {
std::string simOperatorName;
std::string currentModes;
int modePresetIdx = 0;
bool isNetworkRegistered() const {
return mmRegState == "home" || mmRegState == "roaming" ||
nasRegState == "registered" ||
mmState == "registered" || mmState == "connected";
}
bool isNetworkSearching() const {
return mmRegState == "searching" || nasRegState == "searching" ||
mmState == "searching" || mmState == "enabling" ||
mmState == "connecting";
}
std::string regStatusLabel() const {
if (slots[0].needsPuk() || slots[1].needsPuk() ||
mmUnlockRequired == "sim-puk") {
return "SIM PUK Required";
}
if (slots[0].needsPin() || slots[1].needsPin() ||
mmUnlockRequired == "sim-pin" || mmState == "locked") {
return "SIM PIN Required";
}
if (mmRegState == "home" ||
(nasRegState == "registered" && nasRoaming != "on")) {
return "Registered (Home)";
}
if (mmRegState == "roaming" ||
(nasRegState == "registered" && nasRoaming == "on")) {
return "Registered (Roaming)";
}
if (mmState == "connected" || mmState == "registered") {
return "Registered";
}
if (isNetworkSearching()) {
return "Searching...";
}
if (mmRegState == "denied" || nasRegState == "registration-denied") {
return "Registration Denied";
}
if (slots[0].state != "present" && slots[1].state != "present") {
return "No SIM Inserted";
}
if (primaryGwSlot == 0) {
return "SIM Not Activated";
}
if (mmState == "disabled") {
return "Modem Disabled";
}
return "Not Registered";
}
};
struct RadioModePreset {
@@ -201,6 +261,12 @@ ModemStatusData queryModemAndSimStatus() {
if (!uimKv["PRIMARY_GW_SLOT"].empty()) {
d.primaryGwSlot = std::atoi(uimKv["PRIMARY_GW_SLOT"].c_str());
}
d.nasRegState = uimKv["NAS_REG_STATE"];
d.nasOperator = uimKv["NAS_OPERATOR"];
d.nasMcc = uimKv["NAS_MCC"];
d.nasMnc = uimKv["NAS_MNC"];
d.nasRoaming = uimKv["NAS_ROAMING"];
d.nasRadio = uimKv["NAS_RADIO"];
for (int i = 0; i < 2; ++i) {
std::string pfx = "SLOT" + std::to_string(i + 1) + "_";
@@ -244,6 +310,7 @@ ModemStatusData queryModemAndSimStatus() {
d.mmFailedReason = cleanMmVal(mmKv["modem.generic.state-failed-reason"]);
d.mmPowerState = cleanMmVal(mmKv["modem.generic.power-state"]);
d.mmUnlockRequired = cleanMmVal(mmKv["modem.generic.unlock-required"]);
d.mmRegState = cleanMmVal(mmKv["modem.3gpp.registration-state"]);
d.operatorName = cleanMmVal(mmKv["modem.3gpp.operator-name"]);
d.operatorCode = cleanMmVal(mmKv["modem.3gpp.operator-code"]);
d.accessTech = cleanMmVal(mmKv["modem.generic.access-technologies.value[1]"]);
@@ -309,6 +376,16 @@ ModemStatusData queryModemAndSimStatus() {
}
}
if (d.operatorName.empty() && !d.nasOperator.empty()) {
d.operatorName = d.nasOperator;
}
if (d.operatorCode.empty() && !d.nasMcc.empty() && !d.nasMnc.empty()) {
d.operatorCode = d.nasMcc + d.nasMnc;
}
if (d.accessTech.empty() && !d.nasRadio.empty() && d.nasRadio != "none") {
d.accessTech = d.nasRadio;
}
// Sync ModemManager lock info into active slot if QMI didn't report retries
int actIdx = (d.primaryGwSlot >= 1 && d.primaryGwSlot <= 2)
? (d.primaryGwSlot - 1)
@@ -395,8 +472,8 @@ int runModemApp(int argc, char** argv) {
return 0;
}
writeIntFile(daemonPidPath, static_cast<int>(::getpid()));
int fifoFd = setupPluginDaemonFifo("modem");
writeIntFile(daemonPidPath, static_cast<int>(::getpid()));
struct sigaction sa {};
sa.sa_handler = handleSignal;
@@ -702,10 +779,24 @@ int runModemApp(int argc, char** argv) {
line2 = "LOCKED (Enter PIN)";
line2Col = &colOrange;
} else if (s.isActiveProvisioned) {
std::string op = status.operatorName.empty() ? "Ready" : status.operatorName;
if (!status.ownNumber.empty()) op += " " + status.ownNumber;
line2 = op;
line2Col = &colGreen;
std::string reg = status.regStatusLabel();
if (!status.operatorName.empty()) {
reg += " - " + status.operatorName;
}
if (!status.accessTech.empty()) {
reg += " [" + status.accessTech + "]";
}
line2 = reg;
if (status.isNetworkRegistered()) {
line2Col = &colGreen;
} else if (status.isNetworkSearching()) {
line2Col = &colOrange;
} else {
line2Col = &colRed;
}
} else if (status.primaryGwSlot == 0) {
line2 = "Present (Unactivated - Press Select)";
line2Col = &colOrange;
} else {
line2 = "Present (Standby " + s.appType + ")";
line2Col = &colBlue;
@@ -726,6 +817,9 @@ int runModemApp(int argc, char** argv) {
else if (pState == "blocked") pState = "PUK Req";
line3 = pState + " (PIN:" + std::to_string(s.pin1Retries) +
" PUK:" + std::to_string(s.puk1Retries) + ")";
if (s.isActiveProvisioned && !status.ownNumber.empty()) {
line3 += " " + status.ownNumber;
}
} else {
line3 = "Insert nano-SIM & restart/switch";
}
@@ -736,8 +830,18 @@ int runModemApp(int argc, char** argv) {
// Quick Action Rows below the 2 SIM cards (4 rows, 24px each)
int actSlot = (simRowSel == 1) ? 2 : 1;
int otherSlot = (status.preferredSlot == 1) ? 2 : 1;
if (status.primaryGwSlot == 0) {
if (status.slots[1].state == "present" && status.slots[0].state != "present") {
otherSlot = 2;
} else if (status.slots[0].state == "present" && status.slots[1].state != "present") {
otherSlot = 1;
}
}
std::string quickRows[4] = {
std::string(kIconRefresh) + " Switch Active to SIM " + std::to_string(otherSlot),
std::string(kIconRefresh) +
(status.primaryGwSlot == 0
? (" Activate SIM " + std::to_string(otherSlot))
: (" Switch Active to SIM " + std::to_string(otherSlot))),
std::string(kIconUnlock) + " Unlock SIM (PIN / PUK)",
std::string(kIconLock) + " Enable / Disable SIM PIN",
std::string(kIconKey) + " Change SIM PIN Code",
@@ -762,29 +866,31 @@ int runModemApp(int argc, char** argv) {
XSetForeground(dpy, gc, pt.spec.border);
XDrawRectangle(dpy, pix, gc, 4, contentY, kWinWidth - 9, 101);
std::string stStr = status.mmPresent ? status.mmState : "nomodem";
if (!status.mmFailedReason.empty()) {
stStr += " (" + status.mmFailedReason + ")";
}
std::string regStr = status.regStatusLabel();
XftColor* stCol = &colRed;
if (status.mmState == "connected" || status.mmState == "registered") {
if (status.isNetworkRegistered()) {
stCol = &colGreen;
} else if (status.mmState == "searching" || status.mmState == "connecting") {
} else if (status.isNetworkSearching()) {
stCol = &colOrange;
}
drawPango(layoutMain, stCol, 10, contentY + 2, 22,
"State: " + stStr, 185);
"Net: " + regStr, 185);
drawSignalBars(kWinWidth - 32, contentY + 6, status.signalQuality);
std::string opLine = "Operator: " +
(status.operatorName.empty() ? "--" : status.operatorName);
if (!status.operatorCode.empty() &&
status.operatorName != status.operatorCode) {
opLine += " (" + status.operatorCode + ")";
}
if (!status.accessTech.empty()) {
opLine += " [" + status.accessTech + "]";
}
drawPango(layoutSmall, &colLight, 10, contentY + 24, 18, opLine, 220);
std::string sigLine = "Signal: " + std::to_string(status.signalQuality) +
"% | Slot: SIM " +
std::string stStr = status.mmPresent ? status.mmState : "nomodem";
std::string sigLine = "Modem: " + stStr + " | Sig: " +
std::to_string(status.signalQuality) + "% | SIM " +
(status.primaryGwSlot > 0 ? std::to_string(status.primaryGwSlot) : "--");
drawPango(layoutSmall, &colLight, 10, contentY + 42, 18, sigLine, 220);
@@ -1090,6 +1196,15 @@ int runModemApp(int argc, char** argv) {
};
auto showWindow = [&](const std::string& arg = "") {
std::string a = toLowerStr(trimStr(arg));
if (visible &&
(modal == ModalMode::VerifyPin || modal == ModalMode::UnblockPuk) &&
(a == "unlock" || a == "pin" || a == "puk")) {
XRaiseWindow(dpy, win);
grabKeys();
render();
return;
}
closeOtherPlugins("modem");
refreshData(true);
applyOpenArg(arg);
@@ -1308,16 +1423,36 @@ int runModemApp(int argc, char** argv) {
auto triggerSimRowAction = [&](int row) {
if (row == 0 || row == 1) {
targetSlot = row + 1;
actionSel = 0;
modal = ModalMode::SimActions;
int sNum = row + 1;
const auto& s = status.slots[row];
if (s.needsPin() || s.needsPuk()) {
openPinOrPukModalForSlot(sNum);
} else if (s.state == "present" && !s.isActiveProvisioned) {
switchSimSlotNow(sNum);
} else {
targetSlot = sNum;
actionSel = 0;
modal = ModalMode::SimActions;
}
} else if (row == 2) {
int otherSlot = (status.preferredSlot == 1) ? 2 : 1;
if (status.primaryGwSlot == 0) {
if (status.slots[1].state == "present" && status.slots[0].state != "present") {
otherSlot = 2;
} else if (status.slots[0].state == "present" && status.slots[1].state != "present") {
otherSlot = 1;
}
}
switchSimSlotNow(otherSlot);
} else if (row == 3) {
int slot = (status.primaryGwSlot >= 1 && status.primaryGwSlot <= 2)
? status.primaryGwSlot
: status.preferredSlot;
if (status.slots[0].needsPin() || status.slots[0].needsPuk()) {
slot = 1;
} else if (status.slots[1].needsPin() || status.slots[1].needsPuk()) {
slot = 2;
}
openPinOrPukModalForSlot(slot);
} else if (row == 4) {
targetSlot = (status.primaryGwSlot >= 1 && status.primaryGwSlot <= 2)
@@ -1446,14 +1581,18 @@ int runModemApp(int argc, char** argv) {
ussdResponse.clear();
ussdWaitingReply = false;
} else if (radioRowSel == 2) {
bool powerOn = (status.mmPowerState == "on" ||
status.mmState == "registered" ||
status.mmState == "connected" ||
status.mmState == "searching");
bool powerOn = (status.mmPowerState == "on" &&
status.mmState != "disabled" &&
status.mmState != "failed");
setBanner(powerOn ? "Disabling modem..." : "Enabling modem...");
render();
runCaptureCmd(powerOn ? "mmcli -m any -d >/dev/null 2>&1"
: "mmcli -m any -e >/dev/null 2>&1");
if (powerOn) {
runCaptureCmd("mmcli -m any -d >/dev/null 2>&1");
} else {
runCaptureCmd(
"doas -n /usr/libexec/msm-modem-uim-selection boot >/dev/null 2>&1 "
"|| mmcli -m any -e >/dev/null 2>&1");
}
refreshData(false);
runAsyncCmd("sxmo_hook_statusbar.sh modem >/dev/null 2>&1");
} else if (radioRowSel == 3) {
@@ -1494,6 +1633,9 @@ int runModemApp(int argc, char** argv) {
}
};
int autoCheckStage = isPreload ? 0 : 2;
int64_t nextAutoCheckMs = nowMs() + 2500;
if (!isPreload) {
showWindow(openArg);
}
@@ -1841,6 +1983,28 @@ int runModemApp(int argc, char** argv) {
timeoutMs = static_cast<int>(rRem);
}
}
} else if (autoCheckStage < 2) {
int64_t aRem = nextAutoCheckMs - now;
if (aRem <= 0) {
++autoCheckStage;
nextAutoCheckMs = now + 4500;
refreshData(false);
if (status.slots[0].needsPin() || status.slots[0].needsPuk() ||
status.slots[1].needsPin() || status.slots[1].needsPuk() ||
status.mmState == "locked") {
autoCheckStage = 2;
showWindow("unlock");
} else if ((status.slots[0].state == "present" ||
status.slots[1].state == "present") &&
(status.primaryGwSlot == 0 ||
status.mmState == "disabled")) {
runAsyncCmd(
"doas -n /usr/libexec/msm-modem-uim-selection boot >/dev/null "
"2>&1");
}
} else if (timeoutMs < 0 || aRem < timeoutMs) {
timeoutMs = static_cast<int>(aRem);
}
}
struct pollfd pfds[2];
+15 -14
View File
@@ -183,7 +183,14 @@ inline bool sendPluginCommand(const char* name, const std::string& cmdLine) {
return false;
}
std::string fifoPath = getPluginFifoPath(name);
int fd = ::open(fifoPath.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC);
int fd = -1;
for (int attempt = 0; attempt < 6; ++attempt) {
fd = ::open(fifoPath.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC);
if (fd >= 0) {
break;
}
::usleep(25000);
}
if (fd < 0) {
return false;
}
@@ -1651,20 +1658,12 @@ inline std::string sendModemUssd(const std::string& input,
if (clean.empty()) return "Empty USSD request.";
int rc = 0;
std::string cmd;
if (isReply && !isUssdCode(clean)) {
cmd = "mmcli -m any --3gpp-ussd-respond=" + shellQuote(clean) + " 2>&1";
} else {
cmd = "mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1; "
"mmcli -m any --3gpp-ussd-initiate=" + shellQuote(clean) + " 2>&1";
}
std::string mode = (isReply && !isUssdCode(clean)) ? "respond" : "initiate";
std::string cmd =
"doas -n /usr/libexec/msm-modem-uim-selection ussd " + mode + " " +
shellQuote(clean) + " 2>&1";
std::string out = trimStr(runCaptureCmd(cmd, &rc));
size_t q1 = out.find('\'');
size_t q2 = out.rfind('\'');
if (q1 != std::string::npos && q2 != std::string::npos && q2 > q1) {
out = out.substr(q1 + 1, q2 - q1 - 1);
}
if (out.empty()) {
out = (rc == 0) ? "USSD request completed." : "USSD request failed.";
}
@@ -1698,7 +1697,9 @@ inline std::string sendModemUssd(const std::string& input,
}
inline void cancelModemUssd() {
runAsyncCmd("mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1");
runAsyncCmd(
"doas -n /usr/libexec/msm-modem-uim-selection ussd cancel >/dev/null 2>&1 "
"|| mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1");
}
} // namespace bananarchy