Compare commits

..
10 changed files with 18 additions and 56 deletions

View file

@ -145,8 +145,6 @@ install: all
$(DESTDIR)$(PREFIX)/share/dbus-1/system.d/imsd-ofono.conf
install -Dm644 packaging/90_imsd-ofono.gschema.override \
$(DESTDIR)$(PREFIX)/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
install -Dm644 packaging/80-imsd-ofono.preset \
$(DESTDIR)$(PREFIX)/lib/systemd/system-preset/80-imsd-ofono.preset
$(PCM) $(OBJ) $(O)/tests:
mkdir -p $@

View file

@ -173,8 +173,8 @@ private headers that no distro installs.
Setup:
```sh
apk add imsd-ofono # enables imsd-ofonod via a systemd preset
systemctl enable --now imsd-ofonod # hand installs: system service, org.ofono is a system name
apk add imsd-ofono # or install the files by hand
systemctl enable --now imsd-ofonod # system service; org.ofono is a system name
# point Calls at the ofono provider (the package ships this as a gschema
# override; this is the per-user equivalent)
gsettings set org.gnome.Calls autoload-plugins "['ofono']"
@ -218,17 +218,13 @@ Known gaps, all inherited rather than introduced:
normal dialpad is unaffected — that reaches imsd's own classifier.
- DTMF is forwarded to imsd, where `SendDtmf` is still a stub (RFC 4733 is not
implemented yet) — the same limitation Plasma has.
- **Restart GNOME Calls after restarting `imsd-ofonod` itself.** When the
- **Restart GNOME Calls after restarting `imsd-ofonod` (or imsd).** When the
modem's `VoiceCallManager` interface goes away and comes back, Calls 50.0
adds a new origin but leaves the old origin's `CallAdded` handler connected:
after one such cycle every call is added twice, after a few the stale handler
runs on freed memory and Calls dies with SIGBUS in `g_hash_table_lookup`
(measured on the FP6). Calls-side; an upstream fix belongs in
`plugins/provider/ofono/calls-ofono-provider.c`. To keep imsd's own
restarts and registration drops from triggering it, `imsd-ofonod` lists the
interface permanently: `Online` tracks registration, and `Dial` fails with
"imsd is not registered" / "imsd is not running" instead. Only `org.ofono`
vanishing — this daemon restarting — still cycles the origin.
`plugins/provider/ofono/calls-ofono-provider.c`.
Development, without a phone or root:

View file

@ -41,7 +41,7 @@ import Imsd;
namespace {
// ---- static config (env-overridable, same knobs as imsd.py) ---------------
constexpr const char* Version = "0.3.8";
constexpr const char* Version = "0.3.7";
constexpr const char* BusName = "net.catcrafts.IMS1";
constexpr const char* ObjPath = "/net/catcrafts/IMS1";
constexpr const char* Iface = "net.catcrafts.IMS1";

View file

@ -23,15 +23,9 @@ D-Bus seam a third party can implement.
Consumer-shaped details, verified against calls 50.0
plugins/provider/ofono/ — get these wrong and the plugin silently ignores us:
- the provider watches `org.ofono` on the SYSTEM bus and proxies `/` as
org.ofono.Manager. A modem is an Origin exactly while its `Interfaces`
property lists "org.ofono.VoiceCallManager" — and Calls 50.0 never
disconnects a removed Origin's CallAdded handler, so every withdraw/
re-add cycle leaves a stale handler behind (calls announced twice, then
a crash on freed memory; measured on the FP6). The interface is
therefore listed PERMANENTLY; registration is reported through the
modem's `Online` property and enforced in Dial, which fails legibly
while imsd is down or unregistered. Only this daemon's own restart
(org.ofono vanishing) still cycles the Origin.
org.ofono.Manager. A modem becomes a dialable Origin only while its
`Interfaces` property contains "org.ofono.VoiceCallManager", so that list
is our registration gate — the analogue of the dialerd's deviceUniList.
- VoiceCall `State` strings are parsed as CallsCallState enum nicks:
dialing / alerting / incoming / active / held / disconnected. imsd's
"ringing" (a 180 from the far end) is oFono's "alerting".
@ -165,13 +159,12 @@ GVariant* PropChanged(const char* name, GVariant* value) {
// ---- Modem / VoiceCallManager properties ------------------------------------
bool VoiceUp() { return ImsdUp && Registered; }
// Listed unconditionally: the plugin builds one Origin per appearance of
// this interface and cannot survive a re-appearance (header). Whether calls
// can actually be placed is VoiceUp(), checked in Dial and shown as Online.
// The registration gate: the plugin only builds an Origin while
// org.ofono.VoiceCallManager is listed here.
GVariant* InterfacesValue() {
GVariantBuilder b;
g_variant_builder_init(&b, G_VARIANT_TYPE("as"));
g_variant_builder_add(&b, "s", VcmIface);
if (VoiceUp()) g_variant_builder_add(&b, "s", VcmIface);
return g_variant_builder_end(&b);
}
@ -541,7 +534,7 @@ void HandleVcmMethod(GDBusConnection*, const gchar*, const gchar*, const gchar*,
return;
}
if (!VoiceUp()) {
g_dbus_method_invocation_return_dbus_error(inv, ErrFailed, ImsdUp ? "imsd is not registered" : "imsd is not running");
g_dbus_method_invocation_return_dbus_error(inv, ErrFailed, "imsd is not registered");
return;
}
Log(std::format("Dial({})", number));
@ -601,10 +594,11 @@ const GDBusInterfaceVTable VcmVtable = { HandleVcmMethod, nullptr, nullptr, {} }
const GDBusInterfaceVTable SsVtable = { HandleSsMethod, nullptr, nullptr, {} };
// ---- imsd events ------------------------------------------------------------
void PublishVoiceState() {
void PublishInterfaces() {
Emit(ModemPath, ModemIface, "PropertyChanged", PropChanged("Interfaces", InterfacesValue()));
Emit(ModemPath, ModemIface, "PropertyChanged",
PropChanged("Online", g_variant_new_boolean(VoiceUp() ? TRUE : FALSE)));
Log(std::format("voice {}", VoiceUp() ? "registered" : (ImsdUp ? "unregistered" : "unavailable (imsd down)")));
Log(std::format("voice interface {}", VoiceUp() ? "available" : "withdrawn"));
}
void OnImsdSignal(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar* signal, GVariant* params, gpointer) {
@ -646,7 +640,7 @@ void OnImsdSignal(GDBusConnection*, const gchar*, const gchar*, const gchar*, co
g_variant_get(params, "(b)", &reg);
if (Registered == static_cast<bool>(reg)) return;
Registered = reg;
PublishVoiceState();
PublishInterfaces();
}
}
@ -685,7 +679,7 @@ void OnImsdAppeared(GDBusConnection*, const gchar*, const gchar*, gpointer) {
ImsdUp = true;
Log("imsd up");
RefreshFromImsd();
PublishVoiceState();
PublishInterfaces();
}
void OnImsdVanished(GDBusConnection*, const gchar*, gpointer) {
@ -696,7 +690,7 @@ void OnImsdVanished(GDBusConnection*, const gchar*, gpointer) {
std::vector<std::string> live;
for (const auto& [uni, rec] : Calls) live.push_back(uni);
for (const std::string& uni : live) RemoveCall(uni, "network");
PublishVoiceState();
PublishInterfaces();
}
gboolean OnTerm(gpointer loop) {

View file

@ -1,5 +0,0 @@
# SPDX-License-Identifier: GPL-3.0-only
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
# imsd-ofono is the opt-in GNOME Calls backend: installing it is the choice
# to run it (the unit itself refuses to coexist with a real ofonod).
enable imsd-ofonod.service

View file

@ -46,9 +46,4 @@ ofono() {
amove usr/lib/systemd/system/imsd-ofonod.service
amove usr/share/dbus-1/system.d/imsd-ofono.conf
amove usr/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
amove usr/lib/systemd/system-preset/80-imsd-ofono.preset
# enabled on install, like imsd.service in the main package
mkdir -p "$subpkgdir"/etc/systemd/system/multi-user.target.wants
ln -s /usr/lib/systemd/system/imsd-ofonod.service \
"$subpkgdir"/etc/systemd/system/multi-user.target.wants/imsd-ofonod.service
}

View file

@ -70,8 +70,6 @@ package() {
"$pkgdir"/usr/share/dbus-1/system.d/imsd-ofono.conf
install -Dm644 90_imsd-ofono.gschema.override \
"$pkgdir"/usr/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
install -Dm644 80-imsd-ofono.preset \
"$pkgdir"/usr/lib/systemd/system-preset/80-imsd-ofono.preset
}
ofono() {
@ -81,11 +79,6 @@ ofono() {
amove usr/lib/systemd/system/imsd-ofonod.service
amove usr/share/dbus-1/system.d/imsd-ofono.conf
amove usr/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
amove usr/lib/systemd/system-preset/80-imsd-ofono.preset
# enabled on install, like imsd.service in the main package
mkdir -p "$subpkgdir"/etc/systemd/system/multi-user.target.wants
ln -s /usr/lib/systemd/system/imsd-ofonod.service \
"$subpkgdir"/etc/systemd/system/multi-user.target.wants/imsd-ofonod.service
}
systemd() {

View file

@ -72,13 +72,6 @@ fi
lookup_profile() { # $1 = log suffix; sets PROFILE_ID, LOOKUP=done on a usable list
[ "$LOOKUP" = done ] && return 0
if OUT=$(qmicli -d qrtr://0 --wds-get-profile-list=3gpp 2>&1); then
# a list with no profiles at all is the modem still coming up, not
# a modem without an ims profile (dev phone cold boot 2026-09-20
# 16:47: exit 0, no entries, index 2 present a minute later)
if ! printf '%s\n' "$OUT" | grep -q -E '^[[:space:]]*\[[0-9]+\] 3gpp'; then
log "ims profile lookup returned no profiles$1"
return 0
fi
PROFILE_ID=$(printf '%s\n' "$OUT" | awk -v apn="$IMS_APN" '
/^[ \t]*\[[0-9]+\] 3gpp/ { idx = $1; gsub(/[^0-9]/, "", idx) }
/APN:/ { a = $0; sub(/.*APN: '"'"'/, "", a); sub(/'"'"'.*/, "", a);

View file

@ -7,6 +7,5 @@ Comment=Plasma Dialer backend for imsd (replaces plasma-dialer's modem-daemon)
Exec=/usr/bin/imsd-dialerd
Icon=call-start
NoDisplay=true
OnlyShowIn=KDE;
X-KDE-autostart-phase=1
X-KDE-StartupNotify=false

View file

@ -27,7 +27,6 @@ cp "$OFONOD" "$stage/imsd-$VER/imsd-ofonod"
cp packaging/ims-pdn-up.sh packaging/imsd.service \
packaging/imsd-dialerd.desktop packaging/imsd-ofonod.service \
packaging/org.ofono.conf packaging/90_imsd-ofono.gschema.override \
packaging/80-imsd-ofono.preset \
packaging/net.catcrafts.IMS1.conf "$stage/imsd-$VER/"
tar -C "$stage" -czf "imsd-$VER.tar.gz" "imsd-$VER"
echo "wrote imsd-$VER.tar.gz ($(du -h "imsd-$VER.tar.gz" | cut -f1))"