Sxmo/How incoming calls work

From postmarketOS Wiki

Introduction

Caution: This write-up is based on SXMO 1.16.3. If you are running a different version, things might work in a completely different way.

Since SXMO is mostly shell scripts, it's quite easy to follow, if you are familiar with reading and writing those. SXMO uses (customizable) hooks for all kinds of things, like start-up, log-out, picking up, or hanging up calls. When the graphical user interface starts it (by default) runs /usr/share/sxmo/default_hooks/sxmo_hook_start.sh. This is done either via ~/.config/sxmo/sway (wayland) or ~/.config/sxmo/xinit (X11).

Monitoring for incoming calls

The start hook mentioned in the introduction starts a user service (via superd) called sxmo_modemmonitor which then runs the script /usr/bin/sxmo_modemmonitor.sh. The script starts a bunch of infinite loop in the background. One of the loops is watching the DBUS for messages from ModemManager for incoming calls. If it gets an appropriate trigger (a line that starts with "signal") it starts yet another script (/usr/bin/sxmo_modem.sh) to check for incoming calls. These four lines of code are basically the SXMO specific magic that make all of it work:

# Monitor for incoming calls
dbus-monitor --system "interface='org.freedesktop.ModemManager1.Modem.Voice',type='signal',member='CallAdded'" |
        while read -r line; do
                echo "$line" | grep -qE "^signal" && sxmo_modem.sh checkforincomingcalls
        done &

The checkforincomingcalls function in sxmo_modem.sh then extracts the caller ID and forwards it to /usr/bin/sxmo_modemcall.sh. (checkforincomingcalls actually does quite a bit more but that is not in the scope of this write-up.)

Picking up the call

Now we finally come to the part where the user can pick up the call. sxmo_modemcall.sh shows a menu that lets the user decide if they want to pick up, hang up, or ignore the call. In this write-up we're only interested in the part where the user picks up. When picking up the call the first thing that happens is that /usr/bin/sxmo_modemaudio.sh sets up the audio pipeline for calls before accepting the call.

Setting up the audio pipeline for calls

Voice calls via the modem work differently then, e.g. recording and playing back "normal" audio. The tool callaudio performs the heavy lifting for us. sxmo_modemaudio.sh just sends a message via DBUS to tell it to enable the setup for voice calls. It then calls the hook sxmo_hook_call_audio.sh with the parameter "enable". This hook then calls sxmo_modemaudio.sh again to

  1. enable the speaker
  2. disable the speaker
  3. mute the mic
  4. unmute the mic

By doing so, it avoids situations, where either the speaker is enabled or the mic is muted at the beginning of a call.

Picking up the call

After the audio pipeline is setup for calls, it is finally time for sxmo_modemcall.sh to pick up by accepting the call through mmcli. Then sxmo_modemcall.sh shows a menu that lets the user interact with the call (change audio settings, send DTMF tones, hang up...).

Ending the call

There are two situations how a call ends. Either the user on our end decides to hang up or the other party hangs up. If the user selects the hang up option in the in-call menu, sxmo_modecall.sh ends the call by hanging up through mmcli. In either situation (user or other party hangs up) the clean up procedure is started from sxmo_modemmonitor.sh. It not only watches the DBUS for messages from ModemManager for incoming calls (see above) but also for finished calls. If it gets an appropriate trigger (a line that starts with "signal") it starts /usr/bin/sxmo_modem.sh to check for finished calls.

# Monitor for finished calls
dbus-monitor --system "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.freedesktop.ModemManager1.Call'" |
        while read -r line; do
                echo "$line" | grep -qE "^signal" && sxmo_modem.sh checkforfinishedcalls
        done &

The checkforfinishedcalls function in sxmo_modem.sh then deletes the call via mmcli, closes the in-call menu, and calls sxmo_modemaudio.sh to reset_audio.

Resetting audio pipeline

The order is in reverse to the setup. First the reset_audio method in sxmo_modemaudio.sh calls the hook sxmo_hook_call_audio.sh with the parameter "disable". This hook then calls sxmo_modemaudio.sh again to

  1. disable the speaker
  2. enable the speaker

By doing so, the hook avoids situations, where the speaker remains disabled after a call. Finally, the reset_audio method in sxmo_modemaudio.sh sends a message via DBUS to tell callaudio to disable the setup for voice calls.