On-device TTS lifecycle recovery
Date: 2026-07-31
Branch: feature_a2a_task_orchestration_bridge
Device: WRBA1M10KRDWA0925E00614
1. Symptom
After moving the on-device agent to the A2A task orchestration bridge branch:
- Cloud responses were produced, but TTS and the launcher speaking-state transition did not complete.
- Pressing the PUI conversation cancel button terminated the MaumAi process.
- The runtime reported
lateinit property audioManager has not been initialized.
These were not separate TTS, launcher, and cancel defects. They shared one initialization boundary failure.
2. Root cause
DeviceCommunicator.initializeDeviceControlProxy() retained
context.applicationContext so the communicator and Cloud event outbox would
not retain a service instance.
The DeviceAgent connection callback still attempted to recover the service with:
(context as? ForegroundService)?.run { ... }
Because the retained context was an application context, the cast always
failed. DeviceAgent was connected, but the callback that starts
ForegroundService.initializeApp() did not run. STT, TTS, and
AudioPlayManager therefore never reached a valid ready state.
3. Corrected lifecycle contract
The fixed boundary is:
DeviceCommunicatorkeeps application context for safe process lifetime.- DeviceAgent connection and disconnection are delivered through explicit
callbacks, not by casting context back to
ForegroundService. ForegroundServicestarts model initialization after the connected callback.AudioPlayManagerbecomes ready only after TTS initialization succeeds.- Every TTS and effect-sound entrypoint checks audio readiness before playback.
- PUI cancellation is a normal coroutine cancellation path, not a playback failure. It releases the voice session, restores IDLE, and preserves the MaumAi process.
DeviceRuntimeLifecyclePolicy isolates the Android-independent readiness and
callback decisions so they can be unit-tested without a service instance.
4. Deployment safety
The final system APK was platform-signed before replacement.
| Item | Evidence |
|---|---|
| Package | com.skmagic.ondeviceai.agent |
| Version | 1.19.28 |
| Install path | /system/priv-app/MaumAi/MaumAi.apk |
| APK SHA-256 | e3e7915743d392441711ec29c5a52ed293d7e52b3d38bfa53143f00c00cf71ae |
| Certificate SHA-256 | c8a2e9bccf597c2fb6dc66bee293fc13f2fc47ec77bc6b2b0d52c11f51192ab8 |
| Pre-change backup | /home/silogood/work/device_backups/2026-07-31_WRBA1M10KRDWA0925E00614_before_tts_lifecycle_fix/MaumAi.installed.apk |
No Android debug certificate was used.
5. Verification
Source verification
./gradlew :app:testDebugUnitTest --rerun-tasks passed. The new policy tests
cover:
- DeviceAgent callbacks without relying on an Android context cast.
- An already-ready audio manager.
- Lazy audio-manager creation after TTS becomes ready.
- Safe rejection while TTS is unavailable.
Real-device verification
After system APK replacement and reboot:
- DeviceAgent connected to MaumAi.
- LLM initialization started from the connection callback.
- STT initialized.
- TTS initialized at 22,050 Hz.
- Speech recognition initialized.
- AudioTrack and
AudioPlayManagerinitialized. - The text E2E input
안녕produced a Cloud DEF response and audible TTS. - The launcher entered
SpeakingActivity. - PUI cancel during a long TTS emitted
STOP_PLAYING. - The runtime logged
TTS playback cancelledandTTS worker cancelled. - MaumAi PID remained stable.
- The launcher returned to
HomeActivity. - The voice state returned to IDLE.
- No
FATAL EXCEPTIONor uninitializedaudioManagerfailure occurred.
6. Scope
This recovery changes the on-device DeviceAgent-to-ForegroundService initialization and TTS cancellation boundary. It does not change DeviceAgent TaskManager source, Cloud route policy, family classification, or Planner decision rules.