Fix QMI USSD async response handling and prevent st terminal popup

This commit is contained in:
Luxferre
2026-10-25 13:32:17 +02:00
parent fe8ba03941
commit b2216c186f
10 changed files with 148 additions and 15 deletions
+2 -2
View File
@@ -115,8 +115,8 @@ mainloop() {
esac
if [ "$CHANGED" = "1" ]; then
# Fetch message (third argument) in sxmo_ussd.sh to save time in this loop
sxmo_terminal.sh sxmo_ussd.sh "$MODEM" "$TYPE" "" &
# Handle unsolicited USSD in background without spawning a terminal window
sxmo_ussd.sh "$MODEM" "$TYPE" "" &
fi
done &
+33
View File
@@ -0,0 +1,33 @@
#!/bin/sh
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2022 Sxmo Contributors
# include common definitions
# shellcheck source=scripts/core/sxmo_common.sh
. sxmo_common.sh
MODEM="$1"
TYPE="$2"
MESSAGE="${3:-}"
# If xdialer, xmodem, or msm-modem-uim-selection ussd is actively handling USSD,
# do not interfere with the active USSD session.
RDIR="${XDG_RUNTIME_DIR:-/tmp}"
if [ -f "$RDIR/xdialer.pid" ] || [ -f "$RDIR/xmodem.pid" ] || \
pgrep -f "msm-modem-uim-selection ussd" >/dev/null 2>&1; then
exit 0
fi
MM_IFACE="org.freedesktop.ModemManager1"
DBUS_PROP_IFACE="org.freedesktop.DBus.Properties"
USSD_IFACE="org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd"
if [ "$TYPE" = "NetworkNotification" ] || [ "$TYPE" = "NetworkRequest" ]; then
MESSAGE="$(busctl --json=short call "$MM_IFACE" "$MODEM" "$DBUS_PROP_IFACE" Get ss "$USSD_IFACE" "$TYPE" 2>/dev/null | jq -r '.data[].data // empty' 2>/dev/null || true)"
fi
if [ -z "$MESSAGE" ]; then
exit 0
fi
sxmo_notify_user.sh "USSD: $MESSAGE"
Binary file not shown.
Binary file not shown.
@@ -1014,23 +1014,80 @@ case "$cmd" in
echo "ERROR: Empty USSD code"
exit 1
fi
mm_state="$(mmcli -m any -K 2>/dev/null | awk -F': ' '$1 == "modem.generic.state" {print $2; exit}')"
mm_state="$(mmcli -m any -K 2>/dev/null | awk -F': ' '$1 == "modem.generic.state" {print $2; exit}' || true)"
if [ "$mm_state" = "disabled" ] || [ -z "$mm_state" ]; then
mmcli -m any -e >/dev/null 2>&1 || true
sleep 1
fi
modem_dbus_path="$(mmcli -m any -K 2>/dev/null | awk -F': ' '$1 == "modem.dbus-path" {print $2; exit}' || true)"
get_ussd_prop() {
prop_name="$1"
if [ -n "$modem_dbus_path" ] && command -v busctl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
busctl --json=short call org.freedesktop.ModemManager1 "$modem_dbus_path" \
org.freedesktop.DBus.Properties Get ss \
org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd "$prop_name" 2>/dev/null | \
jq -r '.data[].data // empty' 2>/dev/null || true
fi
}
prev_notif="$(get_ussd_prop NetworkNotification)"
prev_req="$(get_ussd_prop NetworkRequest)"
ussd_st="$(mmcli -m any -K 2>/dev/null | awk -F': ' '$1 == "modem.3gpp.ussd.status" || $1 == "modem.3gpp.ussd.state" {print $2; exit}' || true)"
if [ "$ussd_mode" = "respond" ] && [ "$ussd_st" != "user-response" ]; then
case "$ussd_code" in
\**\#|\#*\#)
ussd_mode="initiate"
;;
esac
fi
mm_rc=0
mm_out=""
if [ "$ussd_mode" = "respond" ]; then
mm_out="$(mmcli -m any --3gpp-ussd-respond="$ussd_code" 2>&1)"
mm_rc=$?
mm_out="$(mmcli --timeout=30 -m any --3gpp-ussd-respond="$ussd_code" 2>&1)" || mm_rc=$?
else
mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1 || true
mm_out="$(mmcli -m any --3gpp-ussd-initiate="$ussd_code" 2>&1)"
mm_rc=$?
if [ "$ussd_st" = "active" ] || [ "$ussd_st" = "user-response" ]; then
mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1 || true
fi
mm_out="$(mmcli --timeout=30 -m any --3gpp-ussd-initiate="$ussd_code" 2>&1)" || mm_rc=$?
fi
if [ "$mm_rc" -eq 0 ] && [ -n "$mm_out" ]; then
printf "%s\n" "$mm_out" | sed -e "s/^USSD session initiated; new reply from network: '\(.*\)'$/\1/" -e "s/^USSD response sent; new reply from network: '\(.*\)'$/\1/"
exit 0
reply="$(printf "%s\n" "$mm_out" | sed \
-e "1s/^USSD session initiated; new reply from network: '//" \
-e "1s/^response successfully sent in USSD session; new reply from network: '//" \
-e "1s/^USSD response sent; new reply from network: '//" \
-e "\$s/'$//")"
if [ -n "$reply" ]; then
printf "%s\n" "$reply"
exit 0
fi
fi
# Check if the USSD response arrived asynchronously via D-Bus properties
w=0
while [ "$w" -lt 25 ]; do
cur_req="$(get_ussd_prop NetworkRequest)"
cur_notif="$(get_ussd_prop NetworkNotification)"
if [ -n "$cur_req" ] && [ "$cur_req" != "$prev_req" ]; then
printf "%s\n" "$cur_req"
exit 0
fi
if [ -n "$cur_notif" ] && [ "$cur_notif" != "$prev_notif" ]; then
printf "%s\n" "$cur_notif"
exit 0
fi
cur_st="$(mmcli -m any -K 2>/dev/null | awk -F': ' '$1 == "modem.3gpp.ussd.status" || $1 == "modem.3gpp.ussd.state" {print $2; exit}' || true)"
if [ "$cur_st" != "active" ] && [ "$w" -ge 8 ]; then
if [ -n "$cur_req" ]; then
printf "%s\n" "$cur_req"
exit 0
fi
if [ -n "$cur_notif" ]; then
printf "%s\n" "$cur_notif"
exit 0
fi
break
fi
sleep 0.2
w=$((w + 1))
done
# Fallback to direct AT+CUSD on the active SIM AT port
if [ "$PREFERRED_SIM_SLOT" = "2" ]; then
at_candidates="/dev/wwan0at1 /dev/wwan0at0"
Binary file not shown.
Binary file not shown.
Binary file not shown.
+47 -4
View File
@@ -569,6 +569,7 @@ int runModemApp(int argc, char** argv) {
std::string ussdInput = "*100#";
std::string ussdResponse;
bool ussdWaitingReply = false;
int ussdScrollLine = 0;
// AT Console state
int atFocusRow = 0; // 0 = Input, 1 = Preset/Port, 2 = Output scroll
@@ -1180,10 +1181,28 @@ int runModemApp(int argc, char** argv) {
: ussdResponse;
pango_layout_set_width(layoutSmall, (kWinWidth - 48) * PANGO_SCALE);
pango_layout_set_wrap(layoutSmall, PANGO_WRAP_WORD_CHAR);
pango_layout_set_ellipsize(layoutSmall, PANGO_ELLIPSIZE_NONE);
pango_layout_set_text(layoutSmall, respText.c_str(),
static_cast<int>(respText.size()));
pango_xft_render_layout(xftDraw, &colLight, layoutSmall,
24 * PANGO_SCALE, 116 * PANGO_SCALE);
int lineCount = pango_layout_get_line_count(layoutSmall);
int maxVisLines = 6;
if (ussdScrollLine > std::max(0, lineCount - maxVisLines)) {
ussdScrollLine = std::max(0, lineCount - maxVisLines);
}
int curY = 114;
for (int li = ussdScrollLine;
li < lineCount && (li - ussdScrollLine) < maxVisLines; ++li) {
PangoLayoutLine* pl = pango_layout_get_line_readonly(layoutSmall, li);
if (pl) {
PangoRectangle ink{}, logical{};
pango_layout_line_get_pixel_extents(pl, &ink, &logical);
pango_xft_render_layout_line(
xftDraw, &colLight, pl, 24 * PANGO_SCALE,
(curY - logical.y) * PANGO_SCALE);
curY += std::max(18, logical.height + 2);
}
}
pango_layout_set_width(layoutSmall, -1);
drawSoftkeyBar("Send", "Send", "Close");
}
@@ -1414,11 +1433,16 @@ int runModemApp(int argc, char** argv) {
"\nIMEI 2: " + (im.imei2.empty() ? "--" : im.imei2) +
"\nIMEI SV: " + (im.imeiSv.empty() ? "--" : im.imeiSv);
ussdWaitingReply = false;
ussdScrollLine = 0;
return;
}
ussdResponse = "Sending USSD " + ussdInput + "...";
bool wasWaiting = ussdWaitingReply;
std::string req = ussdInput;
ussdScrollLine = 0;
ussdResponse = "Sending USSD " + req + "...";
render();
ussdResponse = sendModemUssd(ussdInput, ussdWaitingReply, &ussdWaitingReply);
ussdResponse = sendModemUssd(req, wasWaiting && !isUssdCode(req),
&ussdWaitingReply);
if (ussdWaitingReply) {
ussdInput.clear();
}
@@ -1765,10 +1789,29 @@ int runModemApp(int argc, char** argv) {
}
continue;
}
if (sym == XK_Up || sym == XK_KP_Up) {
if (ussdScrollLine > 0) {
--ussdScrollLine;
render();
}
continue;
}
if (sym == XK_Down || sym == XK_KP_Down) {
++ussdScrollLine;
render();
continue;
}
if (sym == XK_BackSpace || sym == XK_Delete) {
if (!ussdInput.empty()) {
ussdInput.pop_back();
render();
} else {
if (ussdWaitingReply) {
cancelModemUssd();
ussdWaitingReply = false;
}
modal = ModalMode::None;
render();
}
continue;
}
+1 -1
View File
@@ -1670,7 +1670,7 @@ inline std::string sendModemUssd(const std::string& input,
if (outSessionActive) {
std::string stOut = runCaptureCmd(
"mmcli -m any -K 2>/dev/null | grep 'modem.3gpp.ussd.state'");
"mmcli -m any -K 2>/dev/null | grep -E 'modem\\.3gpp\\.ussd\\.(status|state)'");
if (stOut.find("user-response") != std::string::npos) {
*outSessionActive = true;
}