Compare commits

..
Author SHA1 Message Date
12a65a5574
callaudioshim 1-r1: own callaudiod's activation file, so Phosh gets the shim
Some checks failed
image / image (push) Has been cancelled
Phosh D-Bus-activates org.mobian_project.CallAudio at startup (cui_init ->
call_audio_init), before any autostart runs. So the stock callaudiod took
the name, segfaulted once, and then refused every mute ("card has no usable
source"): Calls' mute did nothing and Calls asserted on each refusal. Under
Plasma the shim won only because its phase-1 autostart ran first.

The package now installs the activation file at callaudiod's path
(replaces="callaudiod"), pointing at the shim, so whoever asks first gets
the shim. apk keeps the file through callaudiod reinstalls and upgrades,
including both packages upgrading in one transaction (tested in
alpine:edge). Removing the shim removes the file; 'apk fix callaudiod'
restores the stock one. A second instance exits quietly instead of
tracing back.

Tested on the dev phone: Phosh (activation at login, mute silences the
uplink, no Calls assertions) and Plasma (autostart owns the name, the
dialer's mute button follows the mic state).
2026-09-23 17:53:07 +02:00
507dac02b5
Build a Phosh image next to the Plasma Mobile one
FP6IMG_UI selects the postmarketOS UI (plasma-mobile, the default, or
phosh). The package set is the same except that the Phosh image adds
imsd's opt-in imsd-ofono subpackage, the GNOME Calls backend; everything
else in it is UI-agnostic. The archive is named after the UI
(fp6-img-phosh.tar.xz), build-info.txt records it, and the workflow runs
the Phosh build as a second step of the same job with FP6IMG_KEEP_DIST=1
so both archives land in one release. Both knobs are carried across the
su into the build user.

The Phosh variant needs imsd 0.3.8 in the registry: that release ships
the preset that enables imsd-ofonod and keeps GNOME Calls alive across
imsd restarts.
2026-09-20 16:58:49 +02:00
6 changed files with 72 additions and 26 deletions

View file

@ -1,8 +1,9 @@
name: image name: image
# Builds a flashable postmarketOS image for the Fairphone 6 (kernel from # Builds the flashable postmarketOS images for the Fairphone 6 (kernel from
# milos-linux combined-stable + imsd) and publishes it as the rolling # milos-linux combined-stable + imsd) — Plasma Mobile (fp6-img.tar.xz) and
# 'latest' release. # Phosh (fp6-img-phosh.tar.xz) — and publishes both as the rolling 'latest'
# release.
# #
# Runs on the dedicated privileged runner label "pmos" (loop devices for # Runs on the dedicated privileged runner label "pmos" (loop devices for
# pmbootstrap install, qemu-user binfmt on the host for the aarch64 chroots). # pmbootstrap install, qemu-user binfmt on the host for the aarch64 chroots).
@ -34,9 +35,16 @@ jobs:
with: with:
persist-credentials: true persist-credentials: true
- name: Build image - name: Build image (Plasma Mobile)
run: ./build.sh run: ./build.sh
# Second UI from the same package builds (the kernel and the aports
# above are already current in pmbootstrap's work dir, so this is the
# install + export only): fp6-img-phosh.tar.xz next to fp6-img.tar.xz
# in the same release.
- name: Build image (Phosh)
run: FP6IMG_UI=phosh FP6IMG_KEEP_DIST=1 ./build.sh
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:

View file

@ -31,6 +31,11 @@ cd fp6-img
./install.sh ./install.sh
``` ```
Two images are published per release, same kernel and packages, different
shell: `fp6-img.tar.xz` boots **Plasma Mobile** (calls through Plasma
Dialer), `fp6-img-phosh.tar.xz` boots **Phosh** (calls through GNOME Calls,
via imsd's `imsd-ofono` backend). Pick one; the install steps are identical.
`install.sh` erases dtbo, flashes the rootfs, **RAM-boots** the kernel, writes the boot partition from `install.sh` erases dtbo, flashes the rootfs, **RAM-boots** the kernel, writes the boot partition from
inside Linux where the bootloader's NV-wipe cannot see it, then reads the modem inside Linux where the bootloader's NV-wipe cannot see it, then reads the modem
NV back and reports the result. The bootloader never writes `boot`. NV back and reports the result. The bootloader never writes `boot`.

View file

@ -3,22 +3,27 @@
# Earpiece port), leaving plasma-dialer's mute button dead and blocking # Earpiece port), leaving plasma-dialer's mute button dead and blocking
# kde-telephony-daemon 25s per call; see the script header for the full # kde-telephony-daemon 25s per call; see the script header for the full
# write-up. The callaudiod package stays installed (plasma-mobile depends on # write-up. The callaudiod package stays installed (plasma-mobile depends on
# it) — this shim simply owns org.mobian_project.CallAudio first via an # it); this package replaces its D-Bus activation file, so activating
# early-phase autostart, so D-Bus activation of the real daemon never fires. # org.mobian_project.CallAudio starts the shim in any session. Phosh
# Deployed and tested on the development FP6. # activates the name at startup, before autostart runs, and the stock daemon
# used to win there (1-r0). Removing this package removes the activation file
# too; 'apk fix callaudiod' restores callaudiod's. Deployed and tested on the
# development FP6.
maintainer="Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>" maintainer="Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>"
pkgname=callaudioshim pkgname=callaudioshim
pkgver=1 pkgver=1
pkgrel=0 pkgrel=1
pkgdesc="callaudiod replacement shim: working in-call mic mute on the FP6" pkgdesc="callaudiod replacement shim: working in-call mic mute on the FP6"
url="https://forgejo.catcrafts.net/Catcrafts/fp6-img" url="https://forgejo.catcrafts.net/Catcrafts/fp6-img"
arch="noarch" arch="noarch"
license="GPL-3.0-only" license="GPL-3.0-only"
depends="python3 py3-dbus py3-gobject3 wireplumber" depends="python3 py3-dbus py3-gobject3 wireplumber"
replaces="callaudiod"
options="!check" options="!check"
source=" source="
callaudioshim.py callaudioshim.py
callaudioshim.desktop callaudioshim.desktop
org.mobian_project.CallAudio.service
" "
package() { package() {
@ -26,9 +31,12 @@ package() {
"$pkgdir"/usr/libexec/callaudioshim.py "$pkgdir"/usr/libexec/callaudioshim.py
install -Dm644 "$srcdir"/callaudioshim.desktop \ install -Dm644 "$srcdir"/callaudioshim.desktop \
"$pkgdir"/etc/xdg/autostart/callaudioshim.desktop "$pkgdir"/etc/xdg/autostart/callaudioshim.desktop
install -Dm644 "$srcdir"/org.mobian_project.CallAudio.service \
"$pkgdir"/usr/share/dbus-1/services/org.mobian_project.CallAudio.service
} }
sha512sums=" sha512sums="
7ca7d75402ea6c48a7c1c19c5c5ed6e556f2c0133aa6e32a12f5c5a1afc0c791f04ebd40102a4b92b0dd87f9330ae64ada025c344525b9ed17289acd8cb952d7 callaudioshim.py 05995f0f732319559ccd9a7f3891c6363261e52ddbdf13b812fce580dbb816c362bd76da36cb9f1c7ee203d5ce2486762ffe46a7660dd4bc10c8bf2b4cd1a8ca callaudioshim.py
060b76660398115af26463425db5a6a2d634b6bdbf48a8ae488528403f8e086a9ef23470316196e6ccd8cb65e0ebd09bbc0df2ff5f8a830407d98404f439c706 callaudioshim.desktop 060b76660398115af26463425db5a6a2d634b6bdbf48a8ae488528403f8e086a9ef23470316196e6ccd8cb65e0ebd09bbc0df2ff5f8a830407d98404f439c706 callaudioshim.desktop
6b44134e3f3ed0a8c3314b0b0cf9a9e5483850cdfb54f486d23199dd9f5f1ee66d674a391126e803796db69182f980b30a714457a81c9a447e146bc9ba5992bd org.mobian_project.CallAudio.service
" "

View file

@ -37,9 +37,11 @@ interface (verified against callaudiod's org.mobian_project.CallAudio.xml):
dynamic in this setup — journal/audio.md 2026-07-18). SpeakerState dynamic in this setup — journal/audio.md 2026-07-18). SpeakerState
defaults to ON. When an Earpiece UCM device exists these become real. defaults to ON. When an Earpiece UCM device exists these become real.
Stock callaudiod must not own the name: pkill it and mask its D-Bus Stock callaudiod must not own the name. The package replaces callaudiod's
activation (see journal/ims.md s44 deploy notes). Runs as the session user D-Bus activation file with one that starts this shim, so whichever client asks
(autostart .desktop), no root needed. first — Phosh activates the name at startup, before autostart runs — gets the
shim; the KDE early-phase autostart stays for the DialerUtils push at session
start. Runs as the session user, no root needed.
""" """
import subprocess import subprocess
@ -185,7 +187,12 @@ class CallAudio(dbus.service.Object):
def main(): def main():
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
session = dbus.SessionBus() session = dbus.SessionBus()
try:
name = dbus.service.BusName(BUS_NAME, session, do_not_queue=True) # noqa: F841 name = dbus.service.BusName(BUS_NAME, session, do_not_queue=True) # noqa: F841
except dbus.exceptions.NameExistsException:
# activated and autostarted in the same session: the first one serves
log(f"callaudioshim: {BUS_NAME} already owned; exiting")
return
CallAudio(session) CallAudio(session)
log(f"callaudioshim: owning {BUS_NAME}") log(f"callaudioshim: owning {BUS_NAME}")
GLib.MainLoop().run() GLib.MainLoop().run()

View file

@ -0,0 +1,3 @@
[D-BUS Service]
Name=org.mobian_project.CallAudio
Exec=/usr/libexec/callaudioshim.py

View file

@ -30,6 +30,18 @@ KERNEL_BRANCH=combined-stable
# sha256; that gated fresh installs only, never upgrades, and cost a commit # sha256; that gated fresh installs only, never upgrades, and cost a commit
# and an image run per release. # and an image run per release.
REGISTRY=https://forgejo.catcrafts.net/api/packages/Catcrafts/alpine/edge/fp6 REGISTRY=https://forgejo.catcrafts.net/api/packages/Catcrafts/alpine/edge/fp6
# FP6IMG_UI: which postmarketOS UI the image boots into. plasma-mobile (the
# default) gets imsd-dialerd from the imsd package itself; phosh gets the
# opt-in imsd-ofono subpackage (imsd-ofonod + the GNOME Calls plugin
# override) on top. Everything else in the package set is UI-agnostic. The
# archive is named after it: fp6-img.tar.xz for plasma-mobile,
# fp6-img-<ui>.tar.xz otherwise, so one release can carry both.
FP6IMG_UI=${FP6IMG_UI:-plasma-mobile}
case "$FP6IMG_UI" in
plasma-mobile) UI_PACKAGES=; IMG_NAME=fp6-img ;;
phosh) UI_PACKAGES=,imsd-ofono; IMG_NAME=fp6-img-phosh ;;
*) echo "FP6IMG_UI=$FP6IMG_UI: expected plasma-mobile or phosh" >&2; exit 1 ;;
esac
PMAPORTS_REPO=https://gitlab.postmarketos.org/postmarketOS/pmaports.git PMAPORTS_REPO=https://gitlab.postmarketos.org/postmarketOS/pmaports.git
cd "$(dirname "$0")" cd "$(dirname "$0")"
@ -83,11 +95,13 @@ if [ "$(id -u)" = 0 ]; then
id build >/dev/null 2>&1 || adduser -D build id build >/dev/null 2>&1 || adduser -D build
echo 'build ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/build echo 'build ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/build
# empty dist in place (never delete the dir itself: a shell cd'd into it # empty dist in place (never delete the dir itself: a shell cd'd into it
# would poison the next podman invocation's cwd) # would poison the next podman invocation's cwd). FP6IMG_KEEP_DIST=1 keeps
# what is there: the second UI build of one run adds its archive next to
# the first one's.
install -d -o build dist install -d -o build dist
find dist -mindepth 1 -delete [ -n "${FP6IMG_KEEP_DIST:-}" ] || find dist -mindepth 1 -delete
# su scrubs the environment — carry the knobs that matter across it # su scrubs the environment — carry the knobs that matter across it
exec su build -c "FP6IMG_NO_CROSSDIRECT='${FP6IMG_NO_CROSSDIRECT:-}' sh -eu '$PWD/build.sh'" exec su build -c "FP6IMG_NO_CROSSDIRECT='${FP6IMG_NO_CROSSDIRECT:-}' FP6IMG_UI='${FP6IMG_UI:-}' sh -eu '$PWD/build.sh'"
fi fi
# git hosts occasionally hiccup (our forgejo lives on a small VM that also # git hosts occasionally hiccup (our forgejo lives on a small VM that also
@ -218,9 +232,9 @@ cat > "$HOME/.config/pmbootstrap_v3.cfg" <<EOF
[pmbootstrap] [pmbootstrap]
aports = $WORK/pmaports aports = $WORK/pmaports
device = fairphone-fp6 device = fairphone-fp6
ui = plasma-mobile ui = $FP6IMG_UI
systemd = always systemd = always
extra_packages = soc-fairphone-fp6-audio,callaudioshim,imsd,fingerprintd,fingerprintd-systemd,fingerprintd-agent,fp6-device-tweaks,fp6-charging-mode,catcrafts-fp6-repo,postmarketos-base-ui-audio-backend-pipewire,pipewire-pulse,pipewire-echo-cancel extra_packages = soc-fairphone-fp6-audio,callaudioshim,imsd,fingerprintd,fingerprintd-systemd,fingerprintd-agent,fp6-device-tweaks,fp6-charging-mode,catcrafts-fp6-repo,postmarketos-base-ui-audio-backend-pipewire,pipewire-pulse,pipewire-echo-cancel$UI_PACKAGES
EOF EOF
# All four source tarballs are generated locally above, so every checksum # All four source tarballs are generated locally above, so every checksum
@ -273,7 +287,7 @@ if [ ! -f "$REGISTRY_KEY" ]; then
exit 1 exit 1
fi fi
retry "fetch registry packages" python3 ./registry-fetch.py "$REGISTRY" "$REGISTRY_KEY" "$REG_DL" \ retry "fetch registry packages" python3 ./registry-fetch.py "$REGISTRY" "$REGISTRY_KEY" "$REG_DL" \
imsd,imsd-systemd fingerprintd,fingerprintd-systemd,fingerprintd-agent imsd,imsd-systemd,imsd-ofono fingerprintd,fingerprintd-systemd,fingerprintd-agent
ABUILD_KEY=$(echo "$WORKDIR"/config_abuild/*.rsa) ABUILD_KEY=$(echo "$WORKDIR"/config_abuild/*.rsa)
if [ ! -f "$ABUILD_KEY" ]; then if [ ! -f "$ABUILD_KEY" ]; then
echo "expected exactly one abuild key in $WORKDIR/config_abuild" >&2 echo "expected exactly one abuild key in $WORKDIR/config_abuild" >&2
@ -325,21 +339,22 @@ pmbootstrap export "$EXPORT"
# so the archive is built without a second 3 GiB copy on disk. # so the archive is built without a second 3 GiB copy on disk.
STAGE="$WORK/stage" STAGE="$WORK/stage"
rm -rf "$STAGE" rm -rf "$STAGE"
mkdir -p "$STAGE/fp6-img" mkdir -p "$STAGE/$IMG_NAME"
ln -s "$EXPORT/boot.img" "$STAGE/fp6-img/boot.img" ln -s "$EXPORT/boot.img" "$STAGE/$IMG_NAME/boot.img"
ln -s "$EXPORT/fairphone-fp6.img" "$STAGE/fp6-img/fairphone-fp6.img" ln -s "$EXPORT/fairphone-fp6.img" "$STAGE/$IMG_NAME/fairphone-fp6.img"
cp README.md install.sh "$STAGE/fp6-img/" cp README.md install.sh "$STAGE/$IMG_NAME/"
{ {
echo "kernel: $KERNEL_REPO $KERNEL_BRANCH @ $COMMIT" echo "kernel: $KERNEL_REPO $KERNEL_BRANCH @ $COMMIT"
echo "ui: $FP6IMG_UI"
echo "built: $(date -u +%Y-%m-%dT%H:%M:%SZ)" echo "built: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "default login: user / 147147 (same as official postmarketOS images)" echo "default login: user / 147147 (same as official postmarketOS images)"
while read -r _n _v _s; do while read -r _n _v _s; do
printf '%-14s %s %s-%s (newest registry package, sha256 %s)\n' "$_n:" "$REGISTRY" "$_n" "$_v" "$_s" printf '%-14s %s %s-%s (newest registry package, sha256 %s)\n' "$_n:" "$REGISTRY" "$_n" "$_v" "$_s"
done < "$REG_DL/manifest" done < "$REG_DL/manifest"
} > "$STAGE/fp6-img/build-info.txt" } > "$STAGE/$IMG_NAME/build-info.txt"
# sums of the extracted contents # sums of the extracted contents
(cd "$STAGE/fp6-img" && sha256sum -- * > sha256sums.txt) (cd "$STAGE/$IMG_NAME" && sha256sum -- * > sha256sums.txt)
tar -C "$STAGE" -chf - fp6-img | xz -T0 -6 > dist/fp6-img.tar.xz tar -C "$STAGE" -chf - "$IMG_NAME" | xz -T0 -6 > "dist/$IMG_NAME.tar.xz"
rm -rf "$STAGE" rm -rf "$STAGE"
ls -la dist/ ls -la dist/