ofonod: keep VoiceCallManager published across imsd restarts; enable on install
GNOME Calls 50.0 builds one origin per appearance of the modem's VoiceCallManager interface and never disconnects a removed origin's CallAdded handler, so every withdraw/re-add cycle leaves a stale handler behind: calls announced twice after one cycle, a SIGBUS on freed memory after a few (measured on a Fairphone 6). Withdrawing the interface on every imsd restart or registration drop therefore cost the only incoming call path on Phosh, where Calls runs as the session's daemon. List the interface permanently. Registration is reported through the modem's Online property and enforced in Dial, which now fails with "imsd is not registered" or "imsd is not running". Only this daemon's own restart (org.ofono vanishing) still cycles the origin, and the README says to restart Calls after that. Installing the opt-in imsd-ofono subpackage now enables imsd-ofonod (a systemd preset plus the wants symlink, like imsd.service in the main package), and imsd-dialerd's autostart is limited to KDE so a Phosh session does not run the Plasma shim. Verified on the FP6 under Phosh with GNOME Calls' autostarted daemon: outgoing call, incoming call answered on the lock screen, then an imsd restart under the running Calls (Online changes only, no origin cycle) followed by another incoming call added once and answered.
This commit is contained in:
parent
590c61fe0b
commit
540ced4e32
8 changed files with 48 additions and 17 deletions
2
Makefile
2
Makefile
|
|
@ -145,6 +145,8 @@ install: all
|
||||||
$(DESTDIR)$(PREFIX)/share/dbus-1/system.d/imsd-ofono.conf
|
$(DESTDIR)$(PREFIX)/share/dbus-1/system.d/imsd-ofono.conf
|
||||||
install -Dm644 packaging/90_imsd-ofono.gschema.override \
|
install -Dm644 packaging/90_imsd-ofono.gschema.override \
|
||||||
$(DESTDIR)$(PREFIX)/share/glib-2.0/schemas/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:
|
$(PCM) $(OBJ) $(O)/tests:
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -173,8 +173,8 @@ private headers that no distro installs.
|
||||||
Setup:
|
Setup:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
apk add imsd-ofono # or install the files by hand
|
apk add imsd-ofono # enables imsd-ofonod via a systemd preset
|
||||||
systemctl enable --now imsd-ofonod # system service; org.ofono is a system name
|
systemctl enable --now imsd-ofonod # hand installs: system service, org.ofono is a system name
|
||||||
# point Calls at the ofono provider (the package ships this as a gschema
|
# point Calls at the ofono provider (the package ships this as a gschema
|
||||||
# override; this is the per-user equivalent)
|
# override; this is the per-user equivalent)
|
||||||
gsettings set org.gnome.Calls autoload-plugins "['ofono']"
|
gsettings set org.gnome.Calls autoload-plugins "['ofono']"
|
||||||
|
|
@ -218,13 +218,17 @@ Known gaps, all inherited rather than introduced:
|
||||||
normal dialpad is unaffected — that reaches imsd's own classifier.
|
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
|
- DTMF is forwarded to imsd, where `SendDtmf` is still a stub (RFC 4733 is not
|
||||||
implemented yet) — the same limitation Plasma has.
|
implemented yet) — the same limitation Plasma has.
|
||||||
- **Restart GNOME Calls after restarting `imsd-ofonod` (or imsd).** When the
|
- **Restart GNOME Calls after restarting `imsd-ofonod` itself.** When the
|
||||||
modem's `VoiceCallManager` interface goes away and comes back, Calls 50.0
|
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:
|
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
|
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`
|
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
|
(measured on the FP6). Calls-side; an upstream fix belongs in
|
||||||
`plugins/provider/ofono/calls-ofono-provider.c`.
|
`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.
|
||||||
|
|
||||||
Development, without a phone or root:
|
Development, without a phone or root:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,15 @@ D-Bus seam a third party can implement.
|
||||||
Consumer-shaped details, verified against calls 50.0
|
Consumer-shaped details, verified against calls 50.0
|
||||||
plugins/provider/ofono/ — get these wrong and the plugin silently ignores us:
|
plugins/provider/ofono/ — get these wrong and the plugin silently ignores us:
|
||||||
- the provider watches `org.ofono` on the SYSTEM bus and proxies `/` as
|
- the provider watches `org.ofono` on the SYSTEM bus and proxies `/` as
|
||||||
org.ofono.Manager. A modem becomes a dialable Origin only while its
|
org.ofono.Manager. A modem is an Origin exactly while its `Interfaces`
|
||||||
`Interfaces` property contains "org.ofono.VoiceCallManager", so that list
|
property lists "org.ofono.VoiceCallManager" — and Calls 50.0 never
|
||||||
is our registration gate — the analogue of the dialerd's deviceUniList.
|
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.
|
||||||
- VoiceCall `State` strings are parsed as CallsCallState enum nicks:
|
- VoiceCall `State` strings are parsed as CallsCallState enum nicks:
|
||||||
dialing / alerting / incoming / active / held / disconnected. imsd's
|
dialing / alerting / incoming / active / held / disconnected. imsd's
|
||||||
"ringing" (a 180 from the far end) is oFono's "alerting".
|
"ringing" (a 180 from the far end) is oFono's "alerting".
|
||||||
|
|
@ -159,12 +165,13 @@ GVariant* PropChanged(const char* name, GVariant* value) {
|
||||||
// ---- Modem / VoiceCallManager properties ------------------------------------
|
// ---- Modem / VoiceCallManager properties ------------------------------------
|
||||||
bool VoiceUp() { return ImsdUp && Registered; }
|
bool VoiceUp() { return ImsdUp && Registered; }
|
||||||
|
|
||||||
// The registration gate: the plugin only builds an Origin while
|
// Listed unconditionally: the plugin builds one Origin per appearance of
|
||||||
// org.ofono.VoiceCallManager is listed here.
|
// this interface and cannot survive a re-appearance (header). Whether calls
|
||||||
|
// can actually be placed is VoiceUp(), checked in Dial and shown as Online.
|
||||||
GVariant* InterfacesValue() {
|
GVariant* InterfacesValue() {
|
||||||
GVariantBuilder b;
|
GVariantBuilder b;
|
||||||
g_variant_builder_init(&b, G_VARIANT_TYPE("as"));
|
g_variant_builder_init(&b, G_VARIANT_TYPE("as"));
|
||||||
if (VoiceUp()) g_variant_builder_add(&b, "s", VcmIface);
|
g_variant_builder_add(&b, "s", VcmIface);
|
||||||
return g_variant_builder_end(&b);
|
return g_variant_builder_end(&b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -534,7 +541,7 @@ void HandleVcmMethod(GDBusConnection*, const gchar*, const gchar*, const gchar*,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!VoiceUp()) {
|
if (!VoiceUp()) {
|
||||||
g_dbus_method_invocation_return_dbus_error(inv, ErrFailed, "imsd is not registered");
|
g_dbus_method_invocation_return_dbus_error(inv, ErrFailed, ImsdUp ? "imsd is not registered" : "imsd is not running");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log(std::format("Dial({})", number));
|
Log(std::format("Dial({})", number));
|
||||||
|
|
@ -594,11 +601,10 @@ const GDBusInterfaceVTable VcmVtable = { HandleVcmMethod, nullptr, nullptr, {} }
|
||||||
const GDBusInterfaceVTable SsVtable = { HandleSsMethod, nullptr, nullptr, {} };
|
const GDBusInterfaceVTable SsVtable = { HandleSsMethod, nullptr, nullptr, {} };
|
||||||
|
|
||||||
// ---- imsd events ------------------------------------------------------------
|
// ---- imsd events ------------------------------------------------------------
|
||||||
void PublishInterfaces() {
|
void PublishVoiceState() {
|
||||||
Emit(ModemPath, ModemIface, "PropertyChanged", PropChanged("Interfaces", InterfacesValue()));
|
|
||||||
Emit(ModemPath, ModemIface, "PropertyChanged",
|
Emit(ModemPath, ModemIface, "PropertyChanged",
|
||||||
PropChanged("Online", g_variant_new_boolean(VoiceUp() ? TRUE : FALSE)));
|
PropChanged("Online", g_variant_new_boolean(VoiceUp() ? TRUE : FALSE)));
|
||||||
Log(std::format("voice interface {}", VoiceUp() ? "available" : "withdrawn"));
|
Log(std::format("voice {}", VoiceUp() ? "registered" : (ImsdUp ? "unregistered" : "unavailable (imsd down)")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnImsdSignal(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar* signal, GVariant* params, gpointer) {
|
void OnImsdSignal(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar* signal, GVariant* params, gpointer) {
|
||||||
|
|
@ -640,7 +646,7 @@ void OnImsdSignal(GDBusConnection*, const gchar*, const gchar*, const gchar*, co
|
||||||
g_variant_get(params, "(b)", ®);
|
g_variant_get(params, "(b)", ®);
|
||||||
if (Registered == static_cast<bool>(reg)) return;
|
if (Registered == static_cast<bool>(reg)) return;
|
||||||
Registered = reg;
|
Registered = reg;
|
||||||
PublishInterfaces();
|
PublishVoiceState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -679,7 +685,7 @@ void OnImsdAppeared(GDBusConnection*, const gchar*, const gchar*, gpointer) {
|
||||||
ImsdUp = true;
|
ImsdUp = true;
|
||||||
Log("imsd up");
|
Log("imsd up");
|
||||||
RefreshFromImsd();
|
RefreshFromImsd();
|
||||||
PublishInterfaces();
|
PublishVoiceState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnImsdVanished(GDBusConnection*, const gchar*, gpointer) {
|
void OnImsdVanished(GDBusConnection*, const gchar*, gpointer) {
|
||||||
|
|
@ -690,7 +696,7 @@ void OnImsdVanished(GDBusConnection*, const gchar*, gpointer) {
|
||||||
std::vector<std::string> live;
|
std::vector<std::string> live;
|
||||||
for (const auto& [uni, rec] : Calls) live.push_back(uni);
|
for (const auto& [uni, rec] : Calls) live.push_back(uni);
|
||||||
for (const std::string& uni : live) RemoveCall(uni, "network");
|
for (const std::string& uni : live) RemoveCall(uni, "network");
|
||||||
PublishInterfaces();
|
PublishVoiceState();
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean OnTerm(gpointer loop) {
|
gboolean OnTerm(gpointer loop) {
|
||||||
|
|
|
||||||
5
packaging/80-imsd-ofono.preset
Normal file
5
packaging/80-imsd-ofono.preset
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# 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
|
||||||
|
|
@ -46,4 +46,9 @@ ofono() {
|
||||||
amove usr/lib/systemd/system/imsd-ofonod.service
|
amove usr/lib/systemd/system/imsd-ofonod.service
|
||||||
amove usr/share/dbus-1/system.d/imsd-ofono.conf
|
amove usr/share/dbus-1/system.d/imsd-ofono.conf
|
||||||
amove usr/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
|
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,8 @@ package() {
|
||||||
"$pkgdir"/usr/share/dbus-1/system.d/imsd-ofono.conf
|
"$pkgdir"/usr/share/dbus-1/system.d/imsd-ofono.conf
|
||||||
install -Dm644 90_imsd-ofono.gschema.override \
|
install -Dm644 90_imsd-ofono.gschema.override \
|
||||||
"$pkgdir"/usr/share/glib-2.0/schemas/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() {
|
ofono() {
|
||||||
|
|
@ -79,6 +81,11 @@ ofono() {
|
||||||
amove usr/lib/systemd/system/imsd-ofonod.service
|
amove usr/lib/systemd/system/imsd-ofonod.service
|
||||||
amove usr/share/dbus-1/system.d/imsd-ofono.conf
|
amove usr/share/dbus-1/system.d/imsd-ofono.conf
|
||||||
amove usr/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
|
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() {
|
systemd() {
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,6 @@ Comment=Plasma Dialer backend for imsd (replaces plasma-dialer's modem-daemon)
|
||||||
Exec=/usr/bin/imsd-dialerd
|
Exec=/usr/bin/imsd-dialerd
|
||||||
Icon=call-start
|
Icon=call-start
|
||||||
NoDisplay=true
|
NoDisplay=true
|
||||||
|
OnlyShowIn=KDE;
|
||||||
X-KDE-autostart-phase=1
|
X-KDE-autostart-phase=1
|
||||||
X-KDE-StartupNotify=false
|
X-KDE-StartupNotify=false
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ cp "$OFONOD" "$stage/imsd-$VER/imsd-ofonod"
|
||||||
cp packaging/ims-pdn-up.sh packaging/imsd.service \
|
cp packaging/ims-pdn-up.sh packaging/imsd.service \
|
||||||
packaging/imsd-dialerd.desktop packaging/imsd-ofonod.service \
|
packaging/imsd-dialerd.desktop packaging/imsd-ofonod.service \
|
||||||
packaging/org.ofono.conf packaging/90_imsd-ofono.gschema.override \
|
packaging/org.ofono.conf packaging/90_imsd-ofono.gschema.override \
|
||||||
|
packaging/80-imsd-ofono.preset \
|
||||||
packaging/net.catcrafts.IMS1.conf "$stage/imsd-$VER/"
|
packaging/net.catcrafts.IMS1.conf "$stage/imsd-$VER/"
|
||||||
tar -C "$stage" -czf "imsd-$VER.tar.gz" "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))"
|
echo "wrote imsd-$VER.tar.gz ($(du -h "imsd-$VER.tar.gz" | cut -f1))"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue