Skip to content

feat(helper): replace AudioObject IPC with XPC helper service#4

Merged
cheefbird merged 24 commits intomainfrom
refactor/xpc-ipc-refactor
Jan 27, 2026
Merged

feat(helper): replace AudioObject IPC with XPC helper service#4
cheefbird merged 24 commits intomainfrom
refactor/xpc-ipc-refactor

Conversation

@cheefbird
Copy link
Owner

@cheefbird cheefbird commented Jan 27, 2026

Context

The original IPC approach using custom AudioObject properties failed because coreaudiod blocks custom property writes before they reach the driver. Solution follows the BackgroundMusic pattern (BGMXPCHelper) and Apple QA1811: a separate helper process that both host app and driver connect to via XPC.

Key discoveries during implementation:

  • Helper must be LaunchDaemon (not LaunchAgent) - driver runs in system context (Core-Audio-Driver-Service.helper)
  • XPC calls during driver init block even when async - must defer to background queue

Changes Made

New Target: AppFadersHelper

  • XPC listener with MachServices registration
  • VolumeStore for central volume state
  • Separate protocols for host (read-write) and driver (read-only)

Host App

  • DriverBridge rewritten for async XPC
  • AudioOrchestrator updated for async volume operations
  • New DriverError cases for XPC failures

Driver

  • HelperBridge XPC client with local volume cache
  • Removed custom IPC properties from VirtualDevice
  • Added AudioServerPlugIn_MachServices to Info.plist

Scripts

  • install-driver.sh installs helper as LaunchDaemon before driver
  • uninstall-driver.sh removes helper after driver

Testing Notes

Integration test verified (Docs/xpc-integration-test.md):

  • Helper starts on-demand via launchd
  • Host connects to helper via XPC
  • Driver connects to helper via XPC
  • setVolume from host stores in helper
  • getVolume retrieves from helper
  • Driver cache refresh works
  • Uninstall cleanly removes all components

All 30 unit tests pass.

Also adds VS Code launch configurations for AppFadersHelper debug/release.
Add four new error cases for XPC communication failures:
- helperNotRunning: helper service not available
- connectionFailed: XPC connection establishment failed
- connectionInterrupted: XPC connection was interrupted
- remoteError: helper returned an error
Duplicate AppFadersHostProtocol in host target for NSXPCInterface.
Must match helper's definition exactly for XPC to work.
Replace AudioObject property IPC with XPC communication to helper service.
- connect() now async, connects to mach service (no deviceID param)
- setAppVolume/getAppVolume now async throws
- Uses withCheckedThrowingContinuation for XPC reply handling
- Handles connection invalidation and interruption
- Removes all CoreAudio/AudioObject code
Adapt orchestrator to async XPC-based DriverBridge:
- getVolume now async throws
- setVolume now async with internal error handling
- restoreVolumes and handleAppEvent now async
- connect() no longer takes deviceID parameter
@cheefbird cheefbird self-assigned this Jan 27, 2026
Add helper installation before driver:
- Build helper with swift build
- Create /Library/Application Support/AppFaders/
- Copy helper binary with proper permissions
- Install LaunchAgent plist to /Library/LaunchAgents/
- Load LaunchAgent with launchctl
Add helper cleanup after driver removal:
- Unload LaunchAgent with launchctl
- Remove LaunchAgent plist from /Library/LaunchAgents/
- Remove helper binary from support directory
- Remove support directory if empty
- Handle missing files gracefully
- Convert all tests to async
- Remove CoreAudio import and connect(deviceID:) calls
- Update #expect blocks to use await
- Rename deviceNotFound test to helperNotRunning
- Delete orphaned VolumeStoreTests (VolumeStore moved to helper)
Manual test procedure for verifying XPC communication:
- Install/uninstall steps
- Log stream commands for driver and helper
- Volume set/get verification
- Driver cache verification
- Troubleshooting section
- Success criteria checklist
Driver was failing to load because:

1. Helper was a LaunchAgent (user session) but driver runs in system context (Core-Audio-Driver-Service.helper)
2. XPC calls during driver init blocked for 30s causing timeout

Changes:

- Switch helper from LaunchAgent to LaunchDaemon
- Use launchctl bootstrap/bootout for system domain
- Defer initial cache refresh to background queue
@cheefbird cheefbird marked this pull request as ready for review January 27, 2026 03:10
@cheefbird cheefbird merged commit 251166a into main Jan 27, 2026
6 of 7 checks passed
@cheefbird cheefbird deleted the refactor/xpc-ipc-refactor branch February 2, 2026 04:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant