Repository navigation
Echo Show models: checkers, crown, athena, cronos - #70
Conversation
|
@taucher4000 @baudneo I would consider this mergable from a code point of view. |
|
@Gamer92000 Tested d96cb55 on my Echo Show 5 2nd gen (cronos) and it looks good so far. I reran the guided setup on the already rooted Echo ( Checked on the device:
Looks like this PR is ready to be merged into main 👍. |
…penSL ES backend The Echo Show 5 (2019, H23K37) runs the same Fire OS 6574.1 as donut, but as a full Android device: no mixer daemon or libmixerAPI.so, Amazon's front end (libasp) inside the audio HAL, Alexa as apps, Wi-Fi, iptables and Bluetooth owned by Android. Worked out from the firmware only; no unlock is known, so it is not installable. - docs/re-checkers.md: unpacking the block OTA, where the ASR pipeline runs and how the HAL picks it from the AudioRecord source, the audiosignalprocessor binder service (donut's LASP command numbers), stock's wake word path, board facts, what a port still needs. - libpryon.so, libopus.so and libz.so are byte-identical to donut's; donut's pryon_test under qemu detects ALEXA on checkers' en-US model. - devices/checkers/: device.mk, board.c, device.conf (INSTALL=none), kconfig (IKCONFIG, 4.9.77 ARM), probe.md5, stubs. hassmic builds, and links byte-identically against the stubs (tools/mkstubs.sh). - audio_android.c: capture with the VOICE_RECOGNITION preset (the HAL routes it into the ASR pipeline), one player per stream mixed by AudioFlinger, no Bluetooth speaker stream. Untried on a device. - Makefile: device.mk SYSLIBS for NDK libraries beside the stock ones (-lOpenSLES); mkstubs.sh counts libOpenSLES as the NDK's. - ble.c: a model without a stock Bluetooth init service (bt_service NULL). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
amonet-checkers unlocks the Echo Show 5 1st gen and leaves TWRP: a fastboot exploit over micro-USB on a working Echo, or the bootrom through test point TP30 on 2019 units. devices/checkers/README.md has the steps, the boot key combinations it sets up, and the partitions that must never be written (lk, preloader, tee: no way back on most units). What blocks an install now is an install method of this model's own (no A/B slots, dm-verity on system, the policy in boot.img), not the unlock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The XDA thread and the sha256 of amonet-checkers-v2.0.1.zip and boot-root.zip in devices/checkers/README.md. amonet's lk.bin and tz.img are byte-identical to the pinned 8149 firmware's, so the unlock fits it. The thread's boot-root.img (adb root, insecure properties, fosflags cut down) is built from NS6570/6086: its kernel and ramdisk are 6570's, so it would run that kernel and policy under 8149's /system. Written down with the differences; hassmic's install should rather apply the same changes to 8149's own boot.img. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/mkbootroot.py replaces the two files inside the boot ramdisk's cpio stream (default.prop's five properties, init.fosflags.sh byte for byte the XDA file) and re-splices the image: the header page, kernel and every other ramdisk entry keep their exact stock bytes, and Amazon's kernel-signing certificate page trailing the stock image is carried over. The header ID is AOSP mkbootimg's v0 formula: lk does not check it (the XDA image boots with a foreign one too). --check proves the rebuild - the stock image reassembles byte for byte but for the ID; the built boot-root-8149.img (firmware/, sha256 afca0a33..., 8140800 bytes) is not tried on a device. Corrected the README: 8149's ramdisk is a bare gzip stream, only the kernel carries MTK's 512-byte header. Co-Authored-By: ZCode <noreply@z.ai>
The 8149 root boot.img from the last commit would leave adb dead: with ro.secure=0 adbd setcon()s to its --root_seclabel u:r:su:s0 and AOSP 7 adbd dies when that is refused. 8149's policy has su permissive but no rule letting adbd into it (the XDA 6570 policy makes adbd and every app permissive instead). - scripts/mkbootroot.py: --sepolicy FILE replaces the ramdisk's policy, --no-verity drops "verify" from the fstab, --extract and --cpio read the ramdisk. Without options the output is unchanged (afca0a33...). - devices/checkers/sepolicy.rules: donut boot-root's adb rules plus our init -> su rules; applied by donut's magiskpolicy32 under qemu-arm, the diff against stock is exactly those rules. - scripts/install-boot.sh (INSTALL=boot; install-system.sh hands over): builds the boot image from the firmware's own boot.img (pinned sha256; reproducible), checks the Echo's system build from TWRP or the OS, flashes boot only in amonet's hacked fastboot, checks root adb in su, then installs the files with sysinstall.sh as on donut. --boot-only, --uninstall. Never touches lk, preloader or tee. - devices/checkers/hassmic.rc: firewall on boot, satellite at sys.boot_completed. - alexa-off.sh disables Alexa's apps with pm where the model lists packages, notes them; alexa-on.sh (and boot.sh without hassmic.conf) turn exactly those back on; stock-online keeps the updater off. - lockdown.sh: FW_KEEP for rules a model's stock lacks (checkers: DHCP answers, no stock firewall.sh); the updater's uid from packages.list. - otatool remount clears BLKROSET when the kernel refuses (checkers' /system is its own block device). - tools/sdat2img.py for block OTAs; recording rights read from the firmware (root records; other uids need a package: system). Untried on a device. PC-checked: the boot image build end to end with adb stubbed, alexa scripts against a fake pm, all models build, checkers stubs byte-identical, ota_push_test, otatool_test, fake_ha_update, fake_web_artifacts, fake_ha_esphome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ictl.dex) The Show's framework owns the networks: no dhcpcd exists (DHCP runs when WifiService provisions a network it knows), and it re-syncs wpa_supplicant from its own WifiConfigStore, so driving wpa_supplicant as the Dots do cannot get an address and cannot survive a radio toggle. The way in is what stock's own setup apps use (com.amazon.kindle.otter.oobe and com.amazon.ds2.oobe.efd put an android.net.wifi.WifiConfiguration in): the IWifiManager binder, called as root. - tools/mkwifictl.py assembles scripts/device/wifictl.dex from nothing but itself: a one-class Java program as Dalvik bytecode, reflection only (no android.* type references, so the verifier has nothing to resolve but the platform), run as CLASSPATH=<dir>/wifictl.dex app_process / Wifictl <verb>. Its output is shaped like wpa_cli's, so scripts/device/wifi.sh can serve both worlds: status, list_networks, scan_results (the verbs match wpa_cli's), add <ssid hex> <psk hex|-> (addOrUpdateNetwork, hiddenSSID, WPA_PSK), select <id> (enableNetwork(id, true) + reconnect), enable <id> <only|keep>, remove <id>, reassociate, wifi-on (setWifiEnabled; Fire OS's binder takes the caller's name), name <hex> for messages. The WifiService signatures are the ones of this firmware's own framework dex (boot-framework.oat of 8149). The writer keeps the id tables sorted as the format wants and its read() parses the result back before anything is written. - Checked against the firmware's own dexdump under qemu-arm: structural verification passes and the disassembly is the intended program. Running it needs ashmem, which qemu-user has not: that stays for the device. mkwifictl.py --check re-reads and compares the checked-in dex.
…on main) Main grew Drop In (voip_* in audio.h, opus-encoded) and earcons that keep playing while a condition holds; the checkers backend predated both, and the stubs list was missing opus' encoder functions, so nothing linked. - audio_android.c: a voip pstream on the voice stream type (SL_ANDROID_STREAM_VOICE, its own volume and routing in Android); play_earcon_while() feeds the system stream as before and drops what is left of the queue when go() ends instead of draining it (a ringing timer stopped leaves nothing ringing out). - devices/checkers/stubs/libopus.syms: the encoder symbols Drop In calls (tools/mkstubs.sh again); hassmic byte-identical between the firmware libraries and the stubs.
…rom the page scripts/device/wifi.sh keeps its contract with wifi.c and the page in both worlds: the Dots' wpa_supplicant (W) and, on a model with INSTALL=boot, Android's framework through wifictl.dex (A; Q picks). The framework saves every change at once, so the try-out is what remains of the Dots' transaction: the new network goes in and is switched to, and on any failure it is removed and the old ones enabled again — a reboot lands on the old ones either way. Android's DHCP is its own (no dhcpcd): the address on the interface is the lease, and the gateway for the silent-router tell-apart comes from /proc/net/route (no awk on the Echo), pinged to fill the ARP cache as the Dots' online() does. - scripts/wifi-join.sh (the setup step) joins through wifictl for INSTALL=boot: SSID and passphrase as hex, so nothing of either passes a shell; the radio on, add, select, then the address. - deploy.sh pushes the dex next to wifi.sh (trial runs find it too); install-boot.sh and sysinstall.sh put it on the system partition (644: data, not a program). - tools/qrun.sh falls back to toolchain/qemu-arm (devices/checkers/setup.sh puts one there); install-boot.sh accepts that too. - tests: fake_wifi_tools.py answers as WifiService would in the same world the wpa_cli fake serves (add replaces by name, the store is the world, the address comes with the network, WifiInfo quotes the name, the route file follows the connection); fake_web_wifi.py runs its whole scenario list a second time in that world — wrong password, out of reach, no DHCP, silent router, open and exotic names, replace-not-add, busy, replay — through the same build/hassmic-host and browser.
…device scripts/setup.sh offers the model again: its menu entry says untested, and the dry run walks every step. amonet's fastbrick at the unlock (run in the foreground: it wants YES typed, and must not be interrupted once it starts), the rooted boot image at the root step (install-boot.sh --boot-only: root adb with stock Fire OS has none), the build, the egress lock and the Wi-Fi join through wifictl.dex before the Echo ever talks to Amazon, the install. - the files step downloads a pinned qemu-arm into toolchain/ when the PC has none (tools/qrun.sh and install-boot.sh take it from there), and takes magiskpolicy out of donut's boot-root.zip — a copy under firmware/donut/ serves, so a PC that set up a Dot downloads nothing extra. - no wakeword step, as on the Dots now: the settings page's "Download from Amazon" brings the models once the satellite runs. - docs: the checkers README's install section starts with the guided way and says how Wi-Fi joins; docs/re-checkers.md closes the guided-setup, Wi-Fi-join and CI items; DEVELOPMENT.md names mkwifictl.py; CHANGELOG. - CI: checkers back in the build matrix (no kernel step: no Wi-Fi motion module on it).
…the review
The first wifictl.dex passed the firmware's dexdump, which checks structure
only; ART would have refused it: int aget/sget opcodes for objects, lit8
opcodes off by 8, 22b operands swapped, parameters read from the low
registers (Dalvik puts them last), java/lang/List, Iterator, BitSet and
java/lang/io/PrintStream, Class.forName("int"), main passing one argument to
two-argument verbs. What it computed was off too: unhex read 2i as i+3,
scan_results piled every row into one, KeyMgmt bit 0 (NONE, not WPA_PSK), the
page's 64-hex PSK saved as a quoted passphrase, list_networks' flags from the
wrong Status values.
- tools/mkwifictl.py: logical registers mapped to Dalvik's frame, the opcodes
and formats right, cls() for primitive parameter types, setAccessible on
the binder proxy's methods, a keys verb (getPrivilegedConfiguredNetworks).
--check runs a type-flow check over every method; --framework=<rootfs>
resolves every reference and reflected call in 8149's boot*.oat dex files,
KeyMgmt.WPA_PSK's value included.
- tests/unit/wifictl_run.py (make unit): a Dalvik interpreter runs the dex
verb by verb against a fake WifiService. qemu-user cannot run the
firmware's ART, so a device is the rest.
- wifi.sh: WifiService changes a saved network of the same name in place, so
the keys are read first and a failed switch puts the old one back instead
of removing the network the Echo was on (fake_web_wifi.py: a mistyped
password for the current network, both worlds).
- bundle.sh: wifictl.dex rides along for INSTALL=boot models (updates ran
wifi.sh from a folder without it).
- devices/checkers/setup.sh: system.transfer.list; firmware, rootfs and qemu
whatever the build mode or a rooted Echo (the root step makes the boot
image from them); donut's boot-root.zip from firmware/donut/, not the name
of checkers' own.
- wifi-join.sh: the PSK made on the PC, add retried while the radio comes
up, select checked, - for an open network.
- audio_android.c: earcons written a slot at a time with go() in between.
- qrun.sh: the system's qemu-arm first, toolchain/qemu-arm the fallback.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d from the firmware - tests/fake_wifi_join.py (CI): scripts/wifi-join.sh for INSTALL=boot against a fake adb whose shell runs here, with fake_wifi_tools.py as wifictl and ifconfig: the PSK made on the PC, add retried while the radio comes up (the fake answers -1 for as long as told), a wrong password, an open network, a passphrase of the wrong length, a radio that never comes up. wifi-join.sh takes WIFI_CONF, WIFI_JOIN_SECS and WIFI_ADD_SECS for it. - 8149's WifiServiceImpl.setWifiEnabled (wifi-service.jar, under the firmware's dexdump): the package name only feeds the permission-review consent dialog, and review is off (no ro.permission_review_required, framework-res config_permissionReviewRequired false). wifictl's null is fine (docs/re-checkers.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No earcon folder on this firmware: the sounds are resources of stock's apps, every one stored uncompressed (aapt leaves audio alone). sounds.c now falls back from earcon_dir to board.earcon_zip, a table of stock name -> APK entry, and reads the entry out of the zip itself (end record, central directory, local header, every offset checked; stored entries only, 4 MB at most): no install step, nothing copied, an own file in earcon_dir still wins. checkers' table: the wake sound from SpeechInteractionManager (res/7E.mp3 = ful_ui_wakesound_hybrid, its shrunk names mapped back through resources.arsc), privacy and volume from KnightSystemUI, Bluetooth from KnightSettings, the five Drop In sounds from the comms app. 12 of 13: no setup beacon anywhere, Identify keeps the built-in tone. tests/unit/sounds_test.c (make unit): a synthetic APK (stored WAV, a deflated one refused, a missing entry, earcon_dir winning), clean under ASan/UBSan; with the firmware unpacked, every entry of checkers' table decodes from the real APKs (all 48 kHz, 0.34 to 5.5 s). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…full, the keys grabbed main.c drove the Dots' mixer directly (audio_manager_*_prop); now it asks the audio backend: vol_read/vol_write for VOL_MAIN, VOL_TTS and VOL_MUTE. The mixer backend runs the same tools as before (core_spawn: still without waiting); the PC keeps them in memory; OpenSL ES (checkers) sets a gain on each player (SLVolumeItf, 0.4 dB per step, 0 silent; replies on VOL_TTS, the rest on VOL_MAIN), applied before a player's first sample and kept in state/volume. Android's own stream volumes would take level off on top: wifictl.dex gets a volume-max verb (IAudioService getStreamMaxVolume/setStreamVolume for voice call, system and music, as package "root", which 8149's AudioService notes with AppOps for uid 0; Android 7.1's media_cmd has no volume subcommand), run by main.sh at every satellite start on INSTALL=boot models; --framework checks the signatures, wifictl_run.py runs it against a fake AudioService. board.grab_keys (checkers): the keypad EVIOCGRAB'd, else PhoneWindowManager turns its volume too and shows its panel. Host tests unchanged in result (ESPHome, web, Drop In, Sendspin); every model builds without warnings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…essor; input devices by name main.c's three LIPC calls to the front end (listening mode, the wake word's energies for arbitration, the end of a command) are afe_listening, afe_arbitration and afe_stream_stopped in audio.h now: the same LIPC tools in audio_mixer.c, nothing on the PC. checkers has no LIPC: its libasp runs in the audio HAL behind the binder service audiosignalprocessor, transaction 3 = command(cmd, in, out). afe_parcel.c builds the parcel as "service call ... i32" words for the firmware's own /system/bin/service and reads the reply back out of Android 7's Parcel/HexDump print (format strings checked in its libbinder; the ASCII column ignored, a quote in it included). Commands: SET_LISTENING_MODE 146 and REQUEST_ARBITRATION_JSON 23 (aspclient's constants), NOTIFY_ASR_STREAM_STOPPED 14 and SET_WAKEWORD_METADATA 114 (donut's libgenericaspclient.so pairs the LIPC names with them; checkers' libasp command switch sends 14 to "Stop AFE Diag" and 114 to the size check that names SET_WAKEWORD_METADATA). tests/unit/afe_parcel_test.c in make unit, clean under ASan/UBSan. buttons.c opens "name:<input name>" by EVIOCGNAME; checkers' keypad is name:gpio-keys and its privacy input name:amazon-gating (the kernel's DTBs: no label on gpio-keys, the gating driver's own "mute" device) instead of an event number that depends on the probe order. fake_ha_arbitration, make unit pass; every model builds without warnings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ocessor core_eq/core_set_eq ask afe_eq_get/afe_eq_set (audio.h): LIPC GET/SET_USER_EQ_INFO on the Dots as before, nothing on the PC, and on checkers command 29 / 21 of audiosignalprocessor (donut's libgenericaspclient.so pairs those LIPC names with them, ADJUST 28 and RESET 30 beside). checkers' libasp has the UserEq code and its JSON, behind a vtable, so its handler was not traced: the set sends the JSON as LIPC does, the get uses REQUEST_ARBITRATION_JSON's convention (int32 reply size in, JSON out). Marked [~] in docs/re-checkers.md until a device answers. fake_ha_esphome passes (the EQ entities included); every model builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Echo Show 5 has no action button, and the settings page's login, adb
over Wi-Fi, Improv's authorization and answering a Drop In all wait for
one. board.action_combo: both volume keys pressed within 400 ms of each
other and let go within a second are its press (buttons.c); held 2 s they
still pair for arbitration, one after the other they are volume steps.
The hello says so ("action":"volume_keys"), and the page words its texts for
the Echo they are about (act() in web/app.js: the login, adb, help texts, a
login on another Echo by that one's hello); the factory reset's "hold the
action button 10 s" is left out there.
tests/unit/buttons_test.c (make unit, ASan/UBSan clean): a chord either way
round, one key after the other, presses 600 ms apart, the 2 s pairing hold
with key repeat, a slow chord. fake_web passes; every model builds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first device (a tester's) showed what the PC could not: - 8149's own adbd is a user build: it drops to shell whatever ro.secure, service.adb.root or `fastboot oem flags 61` say. The XDA image's adbd (CyanogenMod's, static, ADBD in device.conf) goes by ro.secure; with it in 8149's ramdisk adb is root in u:r:su:s0 (mkbootroot.py --adbd). The setup takes it out of checkers' own boot-root.zip, and picks up a browser's "boot-root (1).zip" (donut's has the same name). - kaeru 2.0.0 leaves the RTC boot-to-fastboot flag set after `adb reboot bootloader`: every `fastboot reboot` lands in fastboot again. install-boot.sh boots with `fastboot continue` and says to replug the power when it is still in fastboot. - The stock image's certificate page names its sha256; mkbootroot.py leaves it out, as the XDA image does. - setup.sh: the root step and checkers' install run in the foreground, so install-boot.sh's hints show; a booted Echo with adb as shell gets its boot image replaced from there instead of waiting for TWRP. - README: how the firmware .bin gets into TWRP whole; hashes per zlib. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- install-boot.sh: when the boot partition holds the image already and the Echo sits in TWRP, boot it through hacked fastboot and `fastboot continue` (it waited 10 min for a boot that never came). After the install's reboot, a stop in fastboot (kaeru keeps the RTC flag) is continued too. - setup.sh: refuse a path with a space in it (make splits there; a tester's copy "echo-dot-assist-checkers (1)" failed in the build). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l, buttons, light ring) Rebased on main; what main added per model, brought to the Echo Show 5: - Speaker gain: the RT5616's line out "OUT Playback Volume" feeds the speaker amplifier. From checkers' own kernel (soc_mixer_control and TLV, decompressed from boot.img): reg 0x03, inverted, 0..39 = -46.5..+12 dB, 1.5 dB a step; the HAL's /system/etc/audio_device.xml sets 29 (-3 dB) among its initial settings, nothing else names it. board.speaker_gain_db: steps per that many dB, for steps that are not a whole dB (2 per 3 here; a whole dB takes the nearest step). Other models unchanged; spkgain_test gets the Show's shape. - Wake sound removal: audio_android.c sets audio_earcon_started after the first Enqueue (the player is PLAYING from s_open on). - Buttons: the volume-key chord under main's rule (volume steps on release, a pair is no step and no event); its press is Home Assistant's "action" event. buttons_test updated. - Light ring: main.sh leds() skips where /system/etc/led-resources is missing (no ledcontroller). - Drop In blueprint: default_name "Echo Show 5", the slug of the project's model (blueprint_dropin checks it), and echo-show-5 among the blueprint's known models. - install-boot.sh no longer writes NAME (the Echo names itself). make unit, every model with and without STUBS=1 (mkstubs: identical), and the PC suites pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous firewall watcher was found by reading every /proc/*/cmdline with a read -d '' loop. On the Echo Show it spun at 100 % CPU on a process that had exited between the open and the read, so ota_watch never ran: an update passed its self test and was never promoted, and root's other requests (reboot, Wi-Fi, artifacts) went unanswered. The Echo 2's tr did the same before (2026-09-30). Now one read of comm (only shells can be the watcher) and one dd read of their cmdline, which ends whatever the read returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the Echo Show there was no mDNS at all, so Home Assistant could not
find the Echo again after an address change:
- D-Bus runs but its policy keeps org.freedesktop.Avahi from root, and
avahi with D-Bus required exits ("Failed to acquire D-Bus name").
avahi_start writes enable-dbus=warn: the service file is announced
without D-Bus; where D-Bus works (the Dots) it is used as before.
- Started without a controlling terminal (main.sh, run.sh), avahi died
about two seconds in, its parent shell with it; from an adb shell it
lived. --no-proc-title (avahi 0.6.31, the Dots' binary too) leaves
argv and the environment alone, and it stays up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mkbootroot.py comments out two lines of stock's init.checkers.rc (any
init.*.rc that has them, RC_OUT):
- setprop service.adb.tcp.port 5555 (on boot): with the image's root
adb and no authentication, every boot opened a root shell to the
network until lockdown.sh closed it. adb over Wi-Fi is hassmic's to
open (adb_gate).
- start bootsound_anim: Amazon's boot chime, played at the media
stream's volume, which on a satellite is not Amazon's to pick.
New image sha256 03253eed... (README); install-boot.sh flashes it over
an older one, as it compares the partition with what it built.
lockdown.sh closing adb over Wi-Fi restarts adbd, and init kills
adbd's process group with it, the setup's adb shell that runs
lockdown.sh among them ("Service 'adbd' killed by signal 9 ... killing
any children"): the setup's lockdown step failed without a word. For an
Echo still on the older image the step now closes it first and waits
for adb.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- amonet's TWRP asks "Keep System Read only?" at every start; the setup says to keep it read only (nothing writes /system from TWRP, and a system TWRP mounted read-write fails the stock boot image's dm-verity, the way back while /system is untouched). Answering restarts TWRP's USB: install-boot.sh waits up to 2 min for adb instead of checking once, and its failure names adb's state and the screen. - A step "System partition" between the unlock and the root boot image: the first Echo Show came out of the unlock with build 0013054618500, and the root boot image is 8149's. Only system is written, from the image on the PC (tools/sdat2img.py of system.new.dat, reproducible, SYSTEM_IMG_SHA256, exactly the partition's 3253731328 bytes), never with twrp install of the .bin, whose updater-script also writes lk, tee, the preloader and an MBR. It goes in 64 MB pieces: each compared on the Echo first, pushed into TWRP's /tmp, written with toybox dd (TWRP's own dd refuses conv= and truncates after a seek), the page cache dropped and read back. adb exec-in into dd stalled for good at 2.94 of 3.25 GB (TWRP runs it on a pty); a cut-short write goes on where it stopped. - The partition counts as 8149's only by its digest, in the step and in install-boot.sh: build.prop lies in the first pieces, and after the stalled write it said 8149 while directories further in were not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a Bluetooth stream
On the first Echo Show (audio_android.c, OpenSL ES):
- The volume is the music stream's index, set through IAudioService
with the firmware's service tool (setStreamVolume = transaction 3,
getStreamMaxVolume = 10, from IAudioService$Stub's constants in 8149's
boot oat). Fire OS aliases system, voice call and TTS to music, so
every player is on it and it is the one volume. The call runs on a
thread of its own (vol_write is called under core_lock); 0..100 stays
in state/volume and is pushed again at start; a void reply counts as
success. The player's own volume (SLVolumeItf) is not used: Amazon's
stack put a track back to 1.0 some 70 ms in ("TrackVolumeChanged"),
so sounds began at our level and jumped to full. main.sh no longer
forces Android's volumes to their maximum.
- GetPosition stayed at 0 for these players, so every sound counted as
never played. Queued is now what waits in the slots (from the buffer
queue's callbacks); s_drained adds 120 ms for AudioTrack and the HAL
only where a sound's end matters. The earcon wait also ends at the
sound's length + 1 s whatever the player says: it waited for ever
after the first sound, and every later one queued behind it.
- bt_open/bt_write: a player for the Bluetooth stream (a phone playing
to the Echo) beside music; it was -1, so a2dp.c dropped everything.
docs/re-checkers.md: Music Assistant plays, in sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gating driver's input device is "gating" (getevent -il, /dev/input/event0), not "amazon-gating": the mute button was caught only by the once-a-second poll of the latch. It is grabbed like the keypad now: it reports KEY_POWER, and Android's framework played a stock privacy sound for every press (a fallback ringtone once System UI was gone, at the ring stream's level: the "mute sound at full volume"). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pm disable does not control Amazon's apps on Fire OS: System UI is
enabled again at every boot, persistent apps start at boot disabled or
not (amazon.speech.sim, whad, NativeAccessorProxyServices ran with
enabled=2), and without Alexa's speech service much of the rest crashed
in "has stopped" dialogs. System UI also played stock's sounds for
other apps (its NotificationPlayer: the privacy sound over ours) and
threw on purpose without amazon.speech.sim.
- sysinstall.sh takes the folders of the model's STOCK_APPS_OUT off the
partition into /system/hassmic-removed (a rename; PackageManager does
not look there) at install and with every update that becomes the
factory copy, puts back any no longer listed, and all at uninstall.
checkers: every Amazon app folder of /system/app and priv-app
(pm list packages -f: com.amazon.*, amazon.*, Ivona TTS, Arcus, the
usage stats proxy, Sidewalk, System UI, Amazon's Settings), but not
the shared-library providers declared in /system/etc/permissions
(amazon.jackson-19, DeviceMessagingAndroid[Internal]SDK, MetricsApi,
...): with those gone too FusedLocation (in system_server) and
Settings failed and system_server restarted in a loop. Stay: Android's
own packages, amazon.fireos and the vendor Wi-Fi profile manager.
- tools/mkhome.py -> scripts/device/hassmic-home.apk (HOME_APK): an APK
without code declaring android.app.Activity as a black, full-screen,
direct boot aware MAIN/HOME activity, binary manifest and v1
signature written by the tool. Android needs a home before it
finishes booting, and Amazon's Settings, whose FallbackHome was the
last one, cannot start without Amazon's account apps. Without direct
boot awareness: "No home screen found" and no sys.boot_completed.
sysinstall.sh installs it in /system/app/HassmicHome, the bundle and
install-boot.sh carry it.
- alexa-off.sh: package/class entries disable one component (Settings'
SettingsInitializationHelperService); with a setup wizard disabled it
sets device_provisioned and user_setup_complete, and device.conf's
ANDROID_SETTINGS ("namespace/key=value"), old values recorded for
alexa-on.sh; boot.sh and stock-online count those as something to
undo. Amazon's apps in /data/app (a4b, csm) go through ALEXA_PACKAGES.
The setup runs it at the lockdown step, before Wi-Fi: a wiped Fire OS
showed Amazon's setup wizard on the screen.
- sounds.c looks for an earcon APK under /system/hassmic-removed when
it is not at its path.
Result on the tester's Echo: boot in 30 s, one Amazon process left
(amazonfiled, native), about 550 MB of 1 GB free, no dialogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The black home screen kept the backlight at 238 of 255 with stock's 24-day timeout. ANDROID_SETTINGS sets screen_off_timeout to 15 s. With the screen off Android lets the SoC suspend once no wake lock is held (/sys/power/autosleep), and the satellite must keep listening: root's main.sh holds the kernel wake lock WAKE_LOCK while it runs, alexa-on.sh lets go. The capture keeps running with the screen asleep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With Android's stack off /dev/stpbt (IBluetoothManager.disable), hciscan on the Echo Show: HCI reset, address, version, 315 LE adverts in 8 s. The MT7668 behind btmtksdio speaks H4 as the Dots' combo chips do, so ble.c and a2dp.c take it as they are: "controller ready, ACL 32 x 251", the A2DP sink with SBC, AAC, aptX HD and aptX, a Pixel paired and played. app/Bluetooth joins STOCK_APPS_OUT, global/bluetooth_on=0 ANDROID_SETTINGS, DEFAULT_ARGS loses -B. Open: the Bluetooth speaker (Echo to speaker), for which the mix would have to come back through Android's remote submix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stant actions
Only donut's README said so. The guide's phone section, the settings
page's help for "Bluetooth announcements" and the setup's last screen
("Add it in Home Assistant", one tick away) now say it too; without it
a phone connects with the chime only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No remote submix needed: 8149 has AOSP N's audio.a2dp.default.so and the a2dp module in its audio policy, so btout.c serves the HAL's abstract sockets as on the Dots (audioserver connects to them), and the route becomes the backend's (audio.h audio_a2dp_*): the Dots' mixer by LIPC A2DPSourceConnect plus btmanagerd's AIPC service, Android's AudioPolicy by media.audio_policy setDeviceConnectionState (1) and getDeviceConnectionState (2), with C strings built by afe_parcel.c's new svc_cstr. The volume is set again after each move (AudioService keeps one per device), a route left by an earlier run is taken back at start (state/a2dp-route), and one lost to an audioserver restart is moved again (1.6 s from the kill). Players opened while the route moves wait for it: sounds started as the speaker went away were moved mid-sound to the Echo's speaker, with a pop. Keep-awake also runs while AudioFlinger has the output in standby. On the tester's Echo with a PC as the speaker: heard clearly, back on the Echo without a pop, moves in 410-520 ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What the docs said was open or untested and is seen working on the
tester's Echo Show (installed with the guided setup, fixed along the
way): first run, recording as system, the front end's replies and
wake word energies, Android's volume, the equalizer's get and set, the
input devices by their real names ("gating"), earcons, the firewall
with netd running, the setup's Wi-Fi join. README marks the model as
working, setup.sh drops UNTESTED. Also the README's "Each model" line
gets back the E that ecdc40d lost.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A speaker with AVRCP absolute volume used to take over at the level it reported: a PC (PipeWire) said 100 % and then ignored the Echo's volume, so the Echo played at full scale into a speaker at full volume. Now the Echo sets the speaker to its own volume first and keeps scaling the sound itself until the speaker's answer confirms that level (A_PENDING); a refusal, another value or no answer within 3 s leave the volume with the Echo, as stock had it for every speaker. On Android the saved volume (the full scale the mixer played at for such a speaker) is no longer pushed at start before the core has put the Echo's own back: the core's first write sets Android's index, changed or not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Echo Show has no LED ring; stock drew Alexa's attention as a 10 px line over the app in front. Ours keeps the band for itself so any app can be the screen's UI: ringbar.dex (tools/mkringbar.py on a Dalvik assembler now shared with mkwifictl.py, tools/dexasm.py) runs as app_process, sets the window manager's overscan for the band and puts a SurfaceFlinger layer of its own over it, drawing what hassmic sends. ring_band.c is a second ring.h (RING in device.mk): every pattern of the core as the Dots' ring unrolled, 12 virtual LEDs across the width with the talker in the middle, and Home Assistant's "Light ring" light. Setting "Light band" (0..40 px, default 10). Screen power as stock's: a pattern wakes the screen (IPowerManager), timeout 5 min, 1 min muted or in Do not disturb; uninstall gives the overscan back. Not turned towards the talker: the front end's beam events (an IAudioEventListener on audiosignalprocessor) reached a listener on the device, but only 4 of 7 beams are ever sent and they switched several times a second wherever the talker stood (docs/re-checkers.md). Seen on the tester's Echo: the band at 960 x 10, apps at 960 x 470. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Light sensor: Amazon's stk3x1x driver puts a lux attribute on the platform driver als_ps (/sys/bus/platform/drivers/als_ps/lux): an I2C read of the ALS data at every read, scaled by the factory calibration in idme, the same value Android's light sensor reports (93-94 lux on the Echo). Home Assistant gets Illuminance; LED auto brightness is only offered where an engine follows the sensor (core_led_has_auto): ledcontroller's, which the band does not have. Two of the driver's attributes are not readings: cali_Light recalibrates and saves /data/als_cal_data.bin, ir leaves ALS stopped until a reboot (docs/re-checkers.md). Wi-Fi motion: hassmic_rcpi4m.c builds for 32-bit ARM too. checkers' gen4m driver calls nicRxFillRFB at nicRxProcessDataPacket+0x64; SW_RFB_T has ucGroupVLD at +16 and prRxStatusGroup3 at +32 with 4-byte pointers. The bl is rewritten by the kernel's patch_text (built for KGDB, found through kallsyms). Built against kernel.org 4.9.77 with the IKCONFIG and arm-eabi-4.8 (Amazon's Linaro GCC 6.3 is no longer published); vermagic and struct module match the kernel's. On the Echo: hooked, RCPI live, Home Assistant's motion sensor went on and off. CI builds it (kernel cache entry for checkers). alexa-off.sh: the p2p group removal's wpa_cli once never returned on the Echo Show, and the satellite's start with it: timeout 5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Show has no libacehal_dha.so: its attestation key sits behind the binder service amazon.security.dha (version 1, RSA-2048, PSS over the given digest, as the Dots' HAL). dha.c asks it through service call as system and builds the drvV1 token as checkers' Java MAP does (padded n/mac, its key order, cpuid from /proc/cpuinfo, UTC). dha_jwt() takes the registration's serial. afe_parcel.c is common now (svc_blob). Tested on the Echo against amazon.de: registered, echo de-DE and aed de-DE installed; whisper-static is refused for the Show's device type (403 not allowlisted). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* crown: Echo Show 8 1st gen, at the level checkers has
The Show 8 is the same platform as the Show 5: devices/crown (board, device.conf, rc, SELinux rules, kernel config,
stubs, guided setup, unlock notes), CI builds it, docs/re-crown.md has what differs from checkers.
Crown-specific fixes that came with it, all shared with the other Android models:
- Amazon downloads: dha.c builds drvV3 from the DHA service's version 2 (certificate chain's first certificate)
- the wake blip is not clipped by the speaker opening (a cold output gets a 350 ms lead-in)
- the mute latch that reads "on" at power-on is shown, not announced (mute_latch)
- alexa-off.sh asks Android for its packages once instead of once per package (36 s -> 14 s at boot)
- TIMEZONE in hassmic.conf and the setup's question; ADB_WIFI asked in the setup
- the firewall admits ping from the local network
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* crown: speaker gain below stock (down to -6 dB), and restart hassmic only once it has stopped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* crown: a factory reset also forgets a Wi-Fi network that was saved by another user
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* crown: the guided setup's closing notes match what works (screen, Download from Amazon)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Echo Show: a wake sound of our own, a soft two-note chime, where the firmware has none
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* setup.sh: stop when more than one device is on adb, and say how to disconnect the others
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* crown: the guided setup downloads the kernel sources and compiler for Wi-Fi motion
Without them make leaves hassmic_rcpi4m.ko out and the installed Echo has no Wi-Fi motion setting.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Wake sound removal: the echo looked for after the Echo Show's lead-in
The 350 ms of silence queued in front of a sound on a cold output reached earsup.c as part of the echo's delay
(85 ms + 350 > D_MAX 400 ms), so the search missed it. audio_earcon_started now says how much lead-in there was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* setup.sh: stop only for a second USB device; comments back above their functions
Devices on adb over Wi-Fi were already left alone (USB_ONLY, adb -d); a second USB device is what makes adb -d fail.
checkers asks for the time zone and stops on a failed install as crown does; crown's XDA thread by its link.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* crown docs: one status everywhere, unlock steps in our words; ping, TIMEZONE and the chime documented
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* crown: only the port; the changes for every model go to their own PRs
Taken out of this PR, listed in an issue to come back on their own: the guided setup's one-adb-device check, ping
from the local network, TIMEZONE and the setup's time zone and adb over Wi-Fi questions, restart_hassmic, alexa-off.sh's
single package listing, and the synthesized wake chime (the Echo Show plays the blip again). Their docs with them.
Kept, because crown needs them: the DHA version 2 token, the speaker gain below stock, the cold-output lead-in, the mute
latch shown quietly at start and the factory reset's Wi-Fi forget.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* wifi.sh: the gateway's address without mksh's substrings
${g:6:2} and 16# are mksh's; CI runs wifi.sh under dash ("Bad substitution"), so the WifiService pass of
fake_web_wifi.py never found the router. Bytes by sed, numbers by printf's 0x, which dash, bash and toybox take.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* crown docs: the guided setup tried on an Echo Show 8; works in the README's table
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Julian Imhof <julianimhof00@gmail.com>
* athena: Echo Show 8 2nd gen (Android 9, MT8183) A different platform from crown: Android 9 with Treble and enforcing SELinux, rooted in place by saiyaku-athena's zip (INSTALL=running: install-system.sh writes /system/hassmic and patches the vendor policy), the front end inside the audio HAL (OpenSL ES, as crown), a TAS5805m amplifier, a TCS3400 light sensor. devices/athena, docs/re-athena.md. What the platform needed: - the libc's accept/accept4/connect fail with EBADF (netd's fwmarkd denied on su sockets: sepolicy rule; net_accept, net_connect_fd and libcurl's already-connected socket stay as workarounds) - Wi-Fi through Android 9's IWifiManager (package name argument, ParceledListSlice) in wifictl.dex, run as uid system - ringbar.dex builds the SurfaceControl through its Builder on SDK >= 26 - IAudioService's transaction numbers differ on Android 9: setStreamVolume 7, getStreamMaxVolume 14 (the volume keys and Music Assistant's volume did nothing before) - the light sensor's lux from the raw channels as stock's virtual_als does (lux.h) - speaker gain on the TAS5805m's digital volume, up to +9 dB (listened to: a rap track's bass clips above) - Sendspin: unpaired sessions are closed when "Music Assistant without pairing" changes - Drop In: a quiet room's noise no longer counts as the far end talking (NEAR_MIN_DB, far_voice) - "Download from Amazon" through the fireosdhaservice binder service Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * athena: the mute button is answered at the press, not after the driver's second Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * athena: --uninstall restores the SELinux policy in /vendor and reaches its reboot Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * athena: the guided setup unlocks (saiyaku) and roots (boot-root in TWRP) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * docs: the Show 8's action button is a tap of both volume keys, no hold-to-reset Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * athena: Wi-Fi motion (hassmic_rcpi4m on the 4.4.146 kernel) The driver is named gen2 (wlan_mt76x3_sdio) but its receive path is gen4m's, so hassmic_rcpi4m.c runs with its 32-bit defaults. The kernel has no patch_text and read-only module text: the module falls back to set_memory_rw (kallsyms) and a write under stop_machine. CONFIG_MODVERSIONS is on: devices/athena/Module.symvers carries the kernel's CRCs (read out of boot.img's Image), which the Makefile copies into the kernel tree. Tested on a Show 8 2nd gen: RCPI 120 for unicast (0x14009), 129 for broadcasts, the motion sensor switched on when someone walked past. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * athena: the guided setup downloads the kernel sources and compiler for Wi-Fi motion Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * CI: athena builds its Wi-Fi motion module (kernel sources and compiler in the matrix) The build job left out hassmic_rcpi4m.ko for lack of toolchain/linux-4.4.146 and the check that a bundle must not lose its module failed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * athena: only the port; the changes for every model go to their own PRs Taken out of this PR, to come back on their own (as issue 45 did for crown's): the system-call bypass of the libc's accept()/connect() (netio.h net_accept/net_connect_fd, net.c net_open_connected, libcurl's already-connected socket in davs.c and update.c: under __ANDROID__ it changed every model, and athena's cause, netd denied on su sockets, is fixed by its sepolicy.rules), the 200 ms cold-output lead-in and state/lead_ms (checkers and crown share audio_android.c: back to 350), the Drop In gate (NEAR_MIN_DB, far_voice, dropin_far_level and its log line), closing idle unpaired Sendspin sessions, the blueprint's "drop into"/"drop it in" sentences, the GUIDE's factory-reset note for every Show, and docs/known-issues.md (its athena facts are in docs/re-athena.md now). Their docs with them, and the CHANGELOG entry for restart_hassmic, which is not in this tree. Kept, because athena needs them: INSTALL=running and SEPOLICY_FILE, wifictl.dex and ringbar.dex for Android 9, the IAudioService numbers, fireosdhaservice, the TCS3400 lux, speaker_gain_limit, the early mute press, Wi-Fi motion, and the blueprint's model slug. Fixed on the way: - sysinstall.sh: conf_get's fallback to the installed device.conf never ran (mksh ends the subshell when "." is given a missing file: checked with athena's own sh under qemu), and /vendor is remounted only when the policy is written, not for every promoted update - Makefile: Module.symvers is copied at each build of the module, so a changed list reaches it - ring_band.c: the SDK is read before the fork (a property lookup in the child of a threaded process can deadlock) - athena's setup.sh: boot-root.zip is needed whenever the root step flashes it, the policy tool is unpacked again before the install, locals in step_network; device.conf: WPA_SOCKETS is the firmware's /data/vendor/wifi/wpa/sockets, ambienthome, paladin and whad disabled as on crown - tests/unit/buttons_privacy_test.c for the early mute press; the CI step name back to what fake_wifi_join.py covers - docs: one status everywhere (README, devices/README, the model's page, re-athena.md, PLAN, CHANGELOG), unlock.md in our words, the firmware's FTVDB link, the GUIDE's wake sound and speaker gain lines name the two Show 8s Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> Co-authored-by: Julian Imhof <julianimhof00@gmail.com>
…w models (#65) * cronos: Echo Show 5 2nd gen (2021), installed on one; setup fixes for Ubuntu 24.04+ and the Raspberry Pi The 2021 Show 5 runs checkers' platform and Fire OS build (NS65741/8149, its own image): devices/cronos is checkers' with athena's mute driver (gpio-privacy) and the speaker amplifier of the board revision. One firmware serves boards with RT5616/RT5616E, TAS5805M or MAX98396, so board.h gets speaker_gain_alt and spkgain.c takes the first control card 0 has (MAX98396: Speaker Volume A, +4..+21 dB in 1 dB steps by the kernel's TLV, capped at +6 dB). Stubs, probe files, kconfig, pins of the firmware and both XDA zips (amonet's lk/tz are 8149's, the boot-root's adbd is checkers'); the setup stops for a bootloader older than 8149's. docs/re-cronos.md has what differs and the first install. Installed with the guided setup from a Raspberry Pi 4 on Ubuntu 26.04, which found, fixed here for every model: - tools/qrun.sh: Ubuntu 24.04+ refuses users a namespace; qemu runs without one while the PIDs are low, else it says what to do; install-boot.sh shows why the policy patch failed; the setup warns up front and takes qemu-arm from the package manager where its x86_64 download would not run - udev: the rules give the Echos to plugdev too (ssh sessions), cover the Show's adb and fastboot ids, and the Show setups install them - wifi-join.sh: a network saved before by another user is changed as the system user (test in fake_wifi_join.py) - the Drop In blueprint knows the new model Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cronos: only the port; the changes for every model go to their own PRs Taken out of this PR, listed in issue #45 to come back on their own: tools/qrun.sh running qemu without a PID namespace where Ubuntu 24.04+ refuses users one (every model's PC side; install-boot.sh's qrun.log and the setup's warning with it), qemu-arm from the package manager on a PC that is not x86_64 (scripts/lib/setup.sh qemu_arm, for every Show), the udev rules' plugdev group for setups over ssh (every model), and the udev step in checkers' and crown's setups. Their docs with them (CLAUDE.md, DEVELOPMENT.md, FINDINGS.md, the CHANGELOG entries). Kept, because cronos needs them: board.speaker_gain_alt and spkgain.c, wifi-join.sh's fallback to the system user (the first cronos hit it), the Shows' fastboot id in the udev rules and cronos' udev step, install-boot.sh's messages naming the model's own files, and the blueprint's model slug. Fixed on the way: the setup's bootloader check stopped with "older than 6.5.7.0" when it could not read the version at all (no fastboot answer, another format); it says so now. The docs say what the setup needs on a Raspberry Pi until those PRs are in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Julian Imhof <julianimhof00@gmail.com>
What main added since the branch point and the Show models did not have yet: - audio_android.c: audio_music_gain/audio_tts_gain/audio_call_gain (separate volumes, #32), our PCM scaled before the player gets it as audio_mixer.c does (not the player's volume: Amazon's stack puts a track back to 1.0). - ring_band.c: main's ring.h (ring_start's reloaded callback, designs' slots, per-LED layer, rotation, ring_check): none of it on the band, so no slots, nothing to install, ring_per_led() 0. - Makefile: the Show models' unit tests as targets of their own, as main split the rest (make -j unit). Every model builds with STUBS=1; tests/run.py passes (30 of 30). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- a2dp.c: a speaker with absolute volume is asked for out_volume_now() (the loudest kind while the volumes are separate), not the music's: it jumped there on the next upkeep, replies quiet meanwhile. - audio_android.c: state/volume and the A2DP route through statefile.c (#46: a hard reset left an empty file); state/volume written on the volume thread, since vol_write runs under core_lock and the fsyncs take tens of ms. - settings.c: "Light ring rotation" only where the ring is LEDs; the band has none to turn (ring_rotation stays 0). - ring.js: no designs section, and no designs in the copy, where the ring has no animations to replace (ring_nslots 0 on the band; Apply could only fail). - re-checkers.md: separate volumes on Android scale by the Echo Dot 2's curve; the Show's own is not measured (open). - CHANGELOG.md: the branch's date sections had come out below main's as a second run (2026-10-26 and -17 twice); merged by date, newest first. Every model builds with STUBS=1; tests/run.py passes (30 of 30). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Light band (ring_band.c): - The settings page's designs show on the band: ring.c's slot ids and order, drawn live by the band thread, flattened (band position p = LED p + 5, the talker's pair in the middle), never turned. Apply needs no root there (ring_designs_live): state/ring-applied and ring-installed written, the band reloads; only patterns whose design changed start over. - ring_stock: the band's own patterns as .animation files for the page (frames cut where the picture jumps, else every 40 ms). - hassmic_assist_NN: the replies' own volume in cyan, as on the Dots. - Status JSON: band, band_on (the page says when "Light band" is 0). - The band's height comes from state/config (and a pending preset) before ring_start (settings_peek); settings_load leaves it alone: no flash with "Light band" 0, no flip when a preset changes it. - Renderer: argv and environment made before the fork, execve, pipes close-on-exec, every other descriptor closed before exec; a still picture is re-sent when the LED brightness changes, a still light no longer wakes the thread 30 times a second; no 30 s sleep after a failed start; a failed thread start is tried again. Settings page (web/ring.js): the band drawn as the Show shows it; the designer is one editor with a Ring / Band switch on its stage; its Mirror, Move/Turn, Spin/Run, Rainbow and hit test as top-level helpers. Volume: - board.volume_curve (volcurve.c board_vol_db, the Dot 2's where none): checkers measured 2026-10-27 with tools/vol-curve.py and slplay (an Echo Dot 2's micRaw beside it), a point every 5 volume points. - The volume keys' tone plays at the volume they moved (linked kinds included), not at the sounds' own; at 0 the lowest step's. - led_auto/led_level atomic (read from the band's and protocols' threads). - settings: a line longer than the buffer is skipped whole. Tests: ring_band_test (designs, restart on the same state, Apply that cannot write, brightness, renderer descriptors), volcurve_test per model, band_map_test (page vs band, pixel for pixel, and the designer's band helpers), fake_ha_volumes (the keys' tone, a wait that flaked under load). Node tests fail in CI without node. Every model builds with STUBS=1; make unit and tests/run.py pass (30 of 30); on the tester's Echo Show 5: self test, band, designs page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hassmic's stderr reaches the log file through its reader process (clock.c),
so a line can land after the event the test saw: CI failed arb-join
("tags: joined without permission": arb.c adopt() saves state/arbitration
before it logs the join) and sendspin ("the player speaks the server's
dialect": read as soon as the server had the client), both lines present
in the log moments later. Those checks, and the transcript "Stop." one
read right after va.stopped, now wait for the line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I will just consider this good enough to go into beta / |
Brings the Echo Show models to main: Echo Show 5 1st gen (
checkers), Echo Show 8 1st gen (crown, #33), Echo Show 8 2nd gen (athena, #34) and Echo Show 5 2nd gen (cronos, #65). The screen UI (WIP) is not part of it; it stays oncheckers-screen-ui.What
devices/<codename>/: an OpenSL ES audio backend (audio_android.c, the front end inside the audio HAL, reached throughaudiosignalprocessor), the light ring as a band along the bottom of the screen (ring_band.c), Wi-Fi through Android's WifiService (wifictl.dex), Amazon downloads through the DHA service, guided setups, installers (INSTALL=boot/running).#ifdefs): volume, front end and equalizer behindaudio.h(vol_read/vol_write,afe_*); input devices by name and grabbed (board.grab_keys); both volume keys as the action button where there is none (board.action_combo); the speaker gain below stock (crown); stock sounds read out of the apps.Rebase onto main
The branch was 48 commits behind main (separate volumes #32, ring designs and per-LED fill, Sendspin 1.0, statefile #46, split unit targets,
tests/run.py, ...). Rebased commit by commit, conflicts resolved keeping both sides; one commit dropped (main already had the sameddfix inmain.sh). Two commits on top:ddaa45e: what main added that the Show models lacked:audio_*_gaininaudio_android.c, main'sring.hon the band (no slots, nothing to install, no per-LED), the Show tests as their ownmake unittargets.d96cb55: review fixes: the Bluetooth speaker's start volume with separate volumes,state/volumeand the A2DP route throughstatefile.c(written offcore_lock), "Light ring rotation" and the designs section hidden on the band, CHANGELOG date sections merged (the rebase left two runs).Tested
make DEVICE=<each of 7> STUBS=1: every model builds.tests/run.py: 30 of 30.Before merging, on hardware (the rebase merged shared paths without a device):
Echo Dot:
Echo Show (each model as far as its docs say it works):
docs/re-checkers.md)🤖 Generated with Claude Code