Skip to content

Put Alexa's session through this device - #81

Open
bboe wants to merge 1 commit into
mainfrom
voice-proxy
Open

bboe wants to merge 1 commit into
mainfrom
voice-proxy

Conversation

@bboe

@bboe bboe commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Alexa's session runs through the daemon, which passes every byte on to Amazon unchanged. Alexa works as she did. Nothing reads what goes by, and nothing turns it on or off.

Verification

  • Full suite as 32-bit ARM under Docker with -count=2. go vet for linux/arm, gofmt, shellcheck, pre-commit and check-identifiers.sh clean. internal/avs added to the -race list.
  • Nine mutations caught: dropped request headers, dropped query string, unflushed writes, renamed response headers, forced status, unverified property read-back, missing ro remount, unverified release, refused remount.
  • internal/device/voice.go has tests; it was the only file in that package without them.
  • Verified on a Dot, as install, uninstall, reinstall, uninstall. Alexa answers through it, and the second uninstall left nothing: no /data/local/bin, no file under /system, both properties empty, service.d empty.

The same cycle was then driven from Windows (Python 3.9.13, adb 36) and from Linux (Ubuntu 24.04, Python 3.12.3, adb 34), each installing from a release tarball and uninstalling, verified after a reboot. That matters because uninstall.py hands su -c one string with nested double quotes in it, and Windows rebuilds the argument from a command line rather than taking an argv.

That cycle found three bugs, all fixed here:

  • uninstall.py aborted on a path its own later step removes, so the steps after it never ran and the device was left half cleaned. The daemon's shutdown handler is what kept Alexa working.
  • rmdir /data/local/bin ran before the file that lives there was removed, so it could never succeed.
  • am force-stop on the app exits 0 and does nothing, keeping its pid and its session, so the daemon ran with nothing connected to it until the next reboot. It is killed by pid now, and comes back in about 20 seconds.

🤖 Generated with Claude Code

@bboe
bboe force-pushed the voice-proxy branch 8 times, most recently from 121f2f9 to f723f97 Compare October 5, 2026 21:34
Alexa's session runs through the daemon on its way to Amazon, and everything
in it is passed on unchanged. Nothing reads what goes by, and nothing turns it
on or off: something that can be switched off is configuration this slice does
not have.

Two properties outside this daemon's namespace move where the session goes, and
one file under /system is added. That file always lands under the same name, so
uninstall.py names it rather than keeping a record to remember it by, and a test
pins the name. uninstall.py undoes all three and reads each back, before it
decides whether anything else it swept is still present: a property left
pointing at a daemon that is gone means Alexa cannot answer at all, so that
teardown cannot sit behind an early exit.

Alexa's app reads where to send its session when it starts, and only then, so
it is killed and left to come back. am force-stop does not do it: that exits 0,
keeps the pid, and leaves the daemon with nothing connected to it. The kill
happens only when the endpoint actually moved, because the app needs about 20
seconds to return and a daemon restarting after a panic would otherwise keep
Alexa dead for as long as the loop ran. uninstall.py kills it too: with its
session gone the app parks in DisconnectState and stays there, aborting every
wake word without dialling anything, so clearing the properties alone leaves
her unable to answer with nothing saying so.

setprop returns before the value is visible to the getprop that follows it, so
each property is read back until it settles. A redirect that still gives up
clears both, because a host set with no port is the same silence.

The response headers are flushed as soon as they are written, since Amazon
answers the long-lived request with a status and then nothing until she has
something to say. A failure is answered with a failure rather than a status she
never sent, and one failed accept does not end the listener: the app reconnects
by itself, and the properties still point here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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