From 507dac02b5eff6a08aa7a790cd9a7dd508f23f90 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sun, 20 Sep 2026 16:58:49 +0200 Subject: [PATCH 1/2] 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. --- .forgejo/workflows/build.yml | 16 ++++++++++---- README.md | 5 +++++ build.sh | 41 ++++++++++++++++++++++++------------ 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index ea5f6b0..0eff8b4 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -1,8 +1,9 @@ name: image -# Builds a flashable postmarketOS image for the Fairphone 6 (kernel from -# milos-linux combined-stable + imsd) and publishes it as the rolling -# 'latest' release. +# Builds the flashable postmarketOS images for the Fairphone 6 (kernel from +# milos-linux combined-stable + imsd) — Plasma Mobile (fp6-img.tar.xz) and +# 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 # pmbootstrap install, qemu-user binfmt on the host for the aarch64 chroots). @@ -34,9 +35,16 @@ jobs: with: persist-credentials: true - - name: Build image + - name: Build image (Plasma Mobile) 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 uses: actions/upload-artifact@v3 with: diff --git a/README.md b/README.md index 02529ce..aa8ee72 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,11 @@ cd fp6-img ./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 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`. diff --git a/build.sh b/build.sh index 82ae48e..6d9322b 100755 --- a/build.sh +++ b/build.sh @@ -30,6 +30,18 @@ KERNEL_BRANCH=combined-stable # sha256; that gated fresh installs only, never upgrades, and cost a commit # and an image run per release. 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-.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 cd "$(dirname "$0")" @@ -83,11 +95,13 @@ if [ "$(id -u)" = 0 ]; then id build >/dev/null 2>&1 || adduser -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 - # 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 - 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 - 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 # git hosts occasionally hiccup (our forgejo lives on a small VM that also @@ -218,9 +232,9 @@ cat > "$HOME/.config/pmbootstrap_v3.cfg" <&2 @@ -325,21 +339,22 @@ pmbootstrap export "$EXPORT" # so the archive is built without a second 3 GiB copy on disk. STAGE="$WORK/stage" rm -rf "$STAGE" -mkdir -p "$STAGE/fp6-img" -ln -s "$EXPORT/boot.img" "$STAGE/fp6-img/boot.img" -ln -s "$EXPORT/fairphone-fp6.img" "$STAGE/fp6-img/fairphone-fp6.img" -cp README.md install.sh "$STAGE/fp6-img/" +mkdir -p "$STAGE/$IMG_NAME" +ln -s "$EXPORT/boot.img" "$STAGE/$IMG_NAME/boot.img" +ln -s "$EXPORT/fairphone-fp6.img" "$STAGE/$IMG_NAME/fairphone-fp6.img" +cp README.md install.sh "$STAGE/$IMG_NAME/" { echo "kernel: $KERNEL_REPO $KERNEL_BRANCH @ $COMMIT" + echo "ui: $FP6IMG_UI" echo "built: $(date -u +%Y-%m-%dT%H:%M:%SZ)" echo "default login: user / 147147 (same as official postmarketOS images)" while read -r _n _v _s; do printf '%-14s %s %s-%s (newest registry package, sha256 %s)\n' "$_n:" "$REGISTRY" "$_n" "$_v" "$_s" done < "$REG_DL/manifest" -} > "$STAGE/fp6-img/build-info.txt" +} > "$STAGE/$IMG_NAME/build-info.txt" # sums of the extracted contents -(cd "$STAGE/fp6-img" && sha256sum -- * > sha256sums.txt) -tar -C "$STAGE" -chf - fp6-img | xz -T0 -6 > dist/fp6-img.tar.xz +(cd "$STAGE/$IMG_NAME" && sha256sum -- * > sha256sums.txt) +tar -C "$STAGE" -chf - "$IMG_NAME" | xz -T0 -6 > "dist/$IMG_NAME.tar.xz" rm -rf "$STAGE" ls -la dist/ From 12a65a5574c8309e1d3760d4a31b27cb950af681 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Wed, 23 Sep 2026 17:20:26 +0200 Subject: [PATCH 2/2] callaudioshim 1-r1: own callaudiod's activation file, so Phosh gets the shim 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). --- aports/device/callaudioshim/APKBUILD | 18 +++++++++++++----- aports/device/callaudioshim/callaudioshim.py | 15 +++++++++++---- .../org.mobian_project.CallAudio.service | 3 +++ 3 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 aports/device/callaudioshim/org.mobian_project.CallAudio.service diff --git a/aports/device/callaudioshim/APKBUILD b/aports/device/callaudioshim/APKBUILD index 61b7685..f96686e 100644 --- a/aports/device/callaudioshim/APKBUILD +++ b/aports/device/callaudioshim/APKBUILD @@ -3,22 +3,27 @@ # Earpiece port), leaving plasma-dialer's mute button dead and blocking # kde-telephony-daemon 25s per call; see the script header for the full # write-up. The callaudiod package stays installed (plasma-mobile depends on -# it) — this shim simply owns org.mobian_project.CallAudio first via an -# early-phase autostart, so D-Bus activation of the real daemon never fires. -# Deployed and tested on the development FP6. +# it); this package replaces its D-Bus activation file, so activating +# org.mobian_project.CallAudio starts the shim in any session. Phosh +# 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 " pkgname=callaudioshim pkgver=1 -pkgrel=0 +pkgrel=1 pkgdesc="callaudiod replacement shim: working in-call mic mute on the FP6" url="https://forgejo.catcrafts.net/Catcrafts/fp6-img" arch="noarch" license="GPL-3.0-only" depends="python3 py3-dbus py3-gobject3 wireplumber" +replaces="callaudiod" options="!check" source=" callaudioshim.py callaudioshim.desktop + org.mobian_project.CallAudio.service " package() { @@ -26,9 +31,12 @@ package() { "$pkgdir"/usr/libexec/callaudioshim.py install -Dm644 "$srcdir"/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=" -7ca7d75402ea6c48a7c1c19c5c5ed6e556f2c0133aa6e32a12f5c5a1afc0c791f04ebd40102a4b92b0dd87f9330ae64ada025c344525b9ed17289acd8cb952d7 callaudioshim.py +05995f0f732319559ccd9a7f3891c6363261e52ddbdf13b812fce580dbb816c362bd76da36cb9f1c7ee203d5ce2486762ffe46a7660dd4bc10c8bf2b4cd1a8ca callaudioshim.py 060b76660398115af26463425db5a6a2d634b6bdbf48a8ae488528403f8e086a9ef23470316196e6ccd8cb65e0ebd09bbc0df2ff5f8a830407d98404f439c706 callaudioshim.desktop +6b44134e3f3ed0a8c3314b0b0cf9a9e5483850cdfb54f486d23199dd9f5f1ee66d674a391126e803796db69182f980b30a714457a81c9a447e146bc9ba5992bd org.mobian_project.CallAudio.service " diff --git a/aports/device/callaudioshim/callaudioshim.py b/aports/device/callaudioshim/callaudioshim.py index b935be0..a4ae62c 100644 --- a/aports/device/callaudioshim/callaudioshim.py +++ b/aports/device/callaudioshim/callaudioshim.py @@ -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 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 -activation (see journal/ims.md s44 deploy notes). Runs as the session user -(autostart .desktop), no root needed. +Stock callaudiod must not own the name. The package replaces callaudiod's +D-Bus activation file with one that starts this shim, so whichever client asks +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 @@ -185,7 +187,12 @@ class CallAudio(dbus.service.Object): def main(): dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) session = dbus.SessionBus() - name = dbus.service.BusName(BUS_NAME, session, do_not_queue=True) # noqa: F841 + try: + 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) log(f"callaudioshim: owning {BUS_NAME}") GLib.MainLoop().run() diff --git a/aports/device/callaudioshim/org.mobian_project.CallAudio.service b/aports/device/callaudioshim/org.mobian_project.CallAudio.service new file mode 100644 index 0000000..93109ef --- /dev/null +++ b/aports/device/callaudioshim/org.mobian_project.CallAudio.service @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.mobian_project.CallAudio +Exec=/usr/libexec/callaudioshim.py