#!/bin/bash
# usb-headset-call-mic - make the USB headset microphone work during calls.
#
# The problem: in call mode the audio HAL never sets up a capture path for the
# USB headset. The speech path is routed to input_devices 0x82000000
# (AUDIO_DEVICE_IN_USB_HEADSET) and the USB microphone endpoint even runs at
# the kernel level, but AudioALSACaptureHandlerUsb is never opened:
#   reopenUL() reopen capture handler since output device change
#   reopenUL mCaptureHandler == NULL
# The other side hears nothing.
#
# What helps: an open recording stream on the source. PulseAudio then reports
# the input device to the HAL (setConnectedDevices: IN_HEADSET), the capture
# handler comes into being, and the uplink carries. This service holds such a
# stream - only during a call, and only while a USB headset is the active
# input. The recording goes to /dev/null: nothing is stored, nothing is sent.

set -u

SOURCE="${UHCM_SOURCE:-source.primary_input}"
PORT="${UHCM_PORT:-input-usb_headset}"
SETTLE="${UHCM_SETTLE:-1}"     # seconds after "active" before the stream reopens
HOLD_PID=""

log() { printf 'usb-headset-call-mic: %s\n' "$*"; }

usb_input_active() {
    pactl list sources 2>/dev/null \
        | grep -qE "^[[:space:]]*(Active Port|Aktiver Port): ${PORT}$"
}

start_hold() {
    if [ -n "$HOLD_PID" ] && kill -0 "$HOLD_PID" 2>/dev/null; then
        return
    fi
    if ! usb_input_active; then
        log "no USB headset on the input - no stream needed"
        HOLD_PID=""
        return
    fi
    parec -d "$SOURCE" --raw --channels=1 --rate=48000 > /dev/null 2>&1 &
    HOLD_PID=$!
    log "recording stream open (pid $HOLD_PID), uplink should carry"
}

stop_hold() {
    [ -n "$HOLD_PID" ] || return
    kill "$HOLD_PID" 2>/dev/null
    wait "$HOLD_PID" 2>/dev/null
    log "recording stream closed"
    HOLD_PID=""
}

on_exit() { stop_hold; exit 0; }
trap on_exit TERM INT

calls=0
prop=""

log "started, source=$SOURCE port=$PORT"

# ofono on the system bus is the dependable source of call state: it reports
# every call, no matter who set it up.
while read -r line; do
    case "$line" in
        *"interface=org.ofono.VoiceCallManager"*"member=CallAdded"*)
            calls=$((calls + 1))
            log "call started (open: $calls)"
            start_hold
            ;;
        *"interface=org.ofono.VoiceCallManager"*"member=CallRemoved"*)
            calls=$((calls - 1))
            [ "$calls" -lt 0 ] && calls=0
            log "call ended (open: $calls)"
            [ "$calls" -eq 0 ] && stop_hold
            ;;
        *'string "State"'*)
            prop=State
            ;;
        *'variant'*'string "active"'*)
            if [ "$prop" = State ]; then
                # Only once the call really stands has the HAL finished its
                # routing - the stream opens fresh after that.
                sleep "$SETTLE"
                stop_hold
                start_hold
            fi
            prop=""
            ;;
        *'variant'*'string "disconnected"'*)
            [ "$prop" = State ] && stop_hold
            prop=""
            ;;
        *'string "'*)
            prop=""
            ;;
    esac
done < <(dbus-monitor --system \
            "type='signal',interface='org.ofono.VoiceCallManager'" \
            "type='signal',interface='org.ofono.VoiceCall',member='PropertyChanged'" 2>/dev/null)
