Skip to content

Android and Android TV final QA acceptance packet

Evidence status: This page preserves a dated QA packet. Treat rows marked blocked, pending hardware QA, or release-readiness follow-up as stale evidence until they are rerun on current devices and builds.

This is the fresh Android and Android TV QA evidence packet for the diagnostics-enabled lane. It recreates the useful LOW-154 / LOW-149 acceptance criteria as current documentation and evidence; LOW-154 and LOW-149 remain historical references only and were not modified in code, docs, or Linear state.

AreaDispositionEvidence / follow-up
Android mobile + TV build, unit, lintPassFull Gradle assemble/unit/lint command below returned BUILD SUCCESSFUL; XML totals were 136 tests, 0 failures, 0 errors, 0 skipped for each flavor.
Focused diagnostics testsPassDiagnostics-focused mobile and TV unit command returned BUILD SUCCESSFUL; each flavor ran DiagnosticsCoreTest 7/7 and DiagnosticsUiModelsTest 3/3.
Repository Rust baselinePasscargo fmt, workspace cargo check, and ferrex-core lib tests passed; cargo check emitted existing ferrexctl missing-doc warnings only.
Device / emulator manual QABlocked in this workspaceadb devices -l returned no attached devices. The SDK emulator binary cannot start on this NixOS workspace due to the stub-ld dynamic-loader error, and no Android TV image/AVD is configured. Physical phone/TV evidence remains a release-readiness follow-up.
Screenshots / video / logcatNot capturedNo device was attached. Capture commands and redaction requirements are included below for the release-readiness run.
CodegenNot runNo FlatBuffers schema, generated Kotlin, or Rust contract/codegen files were touched by this docs-only packet.

Run date: 2026-06-14 from /home/lowband/dev/workspaces/ferrex/LOW-379.

Terminal window
cargo fmt --all --check

Result: pass; command exited successfully with no output.

Terminal window
nix develop .#ferrex-player --command cargo check --workspace --all-targets

Result: pass; finished dev profile in 23.30s. Existing ferrexctl missing-documentation warnings were emitted; no errors.

Terminal window
nix develop .#ferrex-player --command cargo test -p ferrex-core --lib

Result: pass; 160 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.89s.

Android mobile + TV assemble / unit / lint

Section titled “Android mobile + TV assemble / unit / lint”
Terminal window
cd mobile/android && ANDROID_HOME=/home/lowband/Android/Sdk ANDROID_SDK_ROOT=/home/lowband/Android/Sdk ./gradlew :app:assembleMobileDebug :app:assembleTvDebug :app:testMobileDebugUnitTest :app:testTvDebugUnitTest :app:lintMobileDebug :app:lintTvDebug --no-daemon --stacktrace -Pandroid.aapt2FromMavenOverride=/home/lowband/Android/Sdk/build-tools/35.0.0/aapt2

Result: pass; BUILD SUCCESSFUL in 8s; 105 actionable tasks: 49 executed, 56 from cache. Lint reports were generated at:

  • mobile/android/app/build/reports/lint-results-mobileDebug.html
  • mobile/android/app/build/reports/lint-results-tvDebug.html

Unit XML totals after the run:

TaskXML suitesResult
testMobileDebugUnitTest26136 tests, 0 failures, 0 errors, 0 skipped
testTvDebugUnitTest26136 tests, 0 failures, 0 errors, 0 skipped
Terminal window
cd mobile/android && ANDROID_HOME=/home/lowband/Android/Sdk ANDROID_SDK_ROOT=/home/lowband/Android/Sdk ./gradlew :app:testMobileDebugUnitTest :app:testTvDebugUnitTest --tests 'com.ferrex.android.core.diagnostics.DiagnosticsCoreTest' --tests 'com.ferrex.android.core.diagnostics.DiagnosticsUiModelsTest' --no-daemon --stacktrace -Pandroid.aapt2FromMavenOverride=/home/lowband/Android/Sdk/build-tools/35.0.0/aapt2

Result: pass; BUILD SUCCESSFUL in 5s; 48 actionable tasks: 1 executed, 47 up-to-date.

Focused XML totals:

Flavor taskFocused diagnostics testsResult
testMobileDebugUnitTestDiagnosticsCoreTest 7/7, DiagnosticsUiModelsTest 3/310 tests, 0 failures, 0 errors, 0 skipped
testTvDebugUnitTestDiagnosticsCoreTest 7/7, DiagnosticsUiModelsTest 3/310 tests, 0 failures, 0 errors, 0 skipped
Terminal window
adb devices -l
adb shell input keyevent KEYCODE_DPAD_CENTER
/home/lowband/Android/Sdk/cmdline-tools/latest/bin/sdkmanager --list_installed | rg '^ system-images|^ emulator|^ platform-tools|^ build-tools;35'
/home/lowband/Android/Sdk/emulator/emulator -list-avds

Results:

  • adb devices -l: only the header List of devices attached; no phone, emulator, or TV target.
  • D-pad key event: adb: no devices/emulators found.
  • Installed SDK entries include build-tools;35.0.0, platform-tools 35.0.2, emulator 35.2.10, and only system-images;android-35;google_apis_playstore;x86_64; no Android TV / Google TV system image is installed.
  • Emulator listing failed before AVD enumeration: NixOS stub-ld dynamic executable error for /home/lowband/Android/Sdk/emulator/emulator.

Not run for this packet because no FlatBuffers schema, shared mobile codegen script, generated Kotlin under mobile/android/app/src/main/java/ferrex/, or Rust contract code was changed. If a later change touches those files, rerun:

Terminal window
nix shell nixpkgs#flatbuffers -c ./mobile/shared/codegen/generate-kotlin.sh
TargetStatus in this workspaceRequired release-readiness follow-up
Phone emulator or physical Android phoneBlocked: no ADB device/emulator attached. Build/unit/lint evidence passed for mobileDebug; no manual phone screenshots/video/logcat captured.Install app/build/outputs/apk/mobile/debug/app-mobile-debug.apk on a phone-class API 28+ emulator/device, run recovery, diagnostics/export, and playback rows below, and attach redacted evidence.
Android TV emulatorBlocked: no Android TV/Google TV AVD configured, only a non-TV Android 35 Play Store image is installed, and the emulator binary fails on this NixOS workspace with stub-ld. Build/unit/lint evidence passed for tvDebug.Provision a runnable Android TV / Google TV AVD, install app/build/outputs/apk/tv/debug/app-tv-debug.apk, run D-pad/OK/Back focus rows below, and attach redacted evidence.
Physical Android TV + remoteBlocked: adb devices -l returned no attached physical TV/remote target.Run the TV matrix on physical hardware before promoting Android TV beyond dev; record model, API level, Android release, resolution, remote type, and redacted captures.

Manual result status for every row is ready / blocked: the recovery behavior is documented, build/unit coverage passed where available, and the exact manual pass criteria are defined, but this workspace had no attached device/emulator for interactive execution.

ScenarioCurrent automated/source evidenceManual pass criteriaManual result
First installFerrexNavGraph and TvFerrexNavGraph route no-server state to server URL entry; TvAuthRecoveryPolicyTest covers initial TV server focus.Fresh install opens explicit server setup, no blank screen/spinner, Back behavior is safe, diagnostics/recovery exits appear when serious failures occur.Blocked: no device.
Bad server URLRecovery models and auth policy tests cover failed connection focus/retry; phone and TV recovery panels expose retry/change/reset.Invalid URL or failed validation returns actionable copy with Retry/Connect, Change server or Reset connection; no OS app-data wipe required.Blocked: no device.
Offline serverRecovery action panels are wired on phone home/recovery/detail/search/player and TV home/grid/detail/search/player surfaces.Previously configured offline server shows retry plus sign-out/change-server/reset diagnostics paths; cached browse data remains usable where present.Blocked: no device.
Expired sessionTokenRefreshAuthenticatorTest, AuthManagerTest, and playback auth-failure tests run in the full Android unit gate.401/403 refresh failure returns to login/recovery with sign in, sign out, change server, reset connection, and diagnostics; no trapped authenticated state.Blocked: no device.
Revoked sessionAuth/session invalidation paths are covered by auth and playback session-invalidated tests.Revoked tokens are cleared or invalidated locally; login/recovery copy explains the session cannot be refreshed and does not require OS app-data wipe.Blocked: no device.
Setup / registration copyPhone login reason copy documents setup-required/registration-closed states; TV policy starts focus on recovery actions for fatal login states.Setup-required and registration-closed states show explanatory copy and recovery actions, not fake setup/PIN flows.Blocked: no device.
Sign outPhone and TV recovery/action panels expose Sign out; auth tests cover token clearing.Sign out clears local auth/session, preserves server URL when expected, returns to login/recovery, and keeps change/reset available.Blocked: no device.
Change serverPhone and TV panels expose Change server from auth, home, cache, detail, search, and playback errors.Change server returns to server URL entry and only adopts a new URL after successful validation.Blocked: no device.
Reset connectionPhone and TV panels expose Reset connection; cache/recovery docs and tests cover server/user scoped cache clearing.Reset clears saved connection and scoped local caches, returns to server URL entry with explanatory copy, and avoids Android OS app-data wipe.Blocked: no device.
Clear selected cachePhone and TV library recovery panels expose selected-cache clear when a library is selected.Selected library cache can be cleared and resynced without clearing auth/server/all app data.Blocked: no device.
Clear all cacheHome/grid recovery supports all-cache clear actions where state allows.All library/image cache can be cleared from in-app recovery and resynced; auth/session and diagnostics are not silently deleted.Blocked: no device.
Clear diagnostics onlyDiagnosticsCoreTest.clearDiagnosticsLeavesAuthAndAppCachesUntouched and DiagnosticsUiModelsTest.reducerKeepsExportFailuresRetryableAndSeparatesClearConfirmation passed for mobile and TV.Clear diagnostics confirmation states that only diagnostic logs, retained crashes, and prior exports are deleted; auth, server, library cache, image cache, and playback state remain intact.Blocked: no device.
ScenarioCurrent evidenceManual pass criteriaManual result
Reachability from settings/homePhone home exposes Settings & Diagnostics; TV home exposes Settings & Diagnostics with focus key settings-diagnostics.From authenticated home, open diagnostics with touch/keyboard on phone and D-pad/OK on TV.Blocked: no device.
Reachability from serious error screensPhone recovery/detail/search/player and TV home/grid/detail/search/player error/recovery panels expose Diagnostics / Export diagnostics.From bad server, cache miss, detail failure, search failure, and playback error panels, diagnostics are reachable without OS app-data wipe.Blocked: no device.
Diagnostics screen contentDiagnosticsSummaryPresenter renders app/build, device/display, server, auth, cache, playback, crash, and privacy rows; TvDiagnosticsScreen and PhoneDiagnosticsScreen share the same reducer/core.Summary loads on phone and TV; Back remains available; TV action panel autofocus does not strand D-pad focus.Blocked: no device.
Export artifact contentsDiagnosticsCoreTest.exportBundleIncludesManifestLogsAndCrashFilesWithRedaction passed. DiagnosticsExportBuilder writes manifest.json, diagnostics.txt, logs/diagnostic-log.txt, and crashes/crash-*.txt entries.Export creates a .zip; unpack and verify manifest, human summary, diagnostic log, and retained crash files are present as applicable.Blocked: no device.
Redaction spot-checksDiagnosticsCoreTest.redactorCoversHeadersQueriesJsonAndBodySecrets, throwableRedactionRemovesSecretsFromMessagesAndStackTraceText, authAndCacheSummariesAvoidRawIdentityAndCountCheapCacheFacts, and playbackLogBridgeUsesSharedRedactorAndFeedsExportableLog passed.Search exported files and screen text for raw server secrets, auth headers, tokens, passwords, PIN/private material, session IDs, local device IDs, usernames/display names where sensitive, and playback ticket URLs before attaching.Blocked: no device.
Clear diagnosticsFocused diagnostics tests passed for clear confirmation and preservation.Clear diagnostics removes prior exports/logs/crashes and then refreshes summary; server URL, auth/session, library/image cache, and playback state still work.Blocked: no device.
Crash-file retentionDiagnosticsCoreTest.crashRetentionRedactsBoundsAndPrunesOldFiles passed. Crash files are bounded/pruned and included in exports.If a retained crash exists, diagnostics shows the count and export includes redacted crash files; clearing diagnostics removes them.Blocked: no device.
Share sheet behaviorDiagnosticsExportShare restricts sharing to .zip files under diagnostics/exports with application/zip and read URI permission; UI reports ActivityNotFoundException as retryable copy.Share sheet opens for an export target; if no target exists, the screen remains recoverable and reports the failure.Blocked: no device.

Source matrix: Android playback QA matrix. Manual phone/TV playback remains blocked by the absent device/emulator; automated evidence listed below passed in the full Android unit gate.

ScenarioAutomated/fake evidencePhone manual resultTV manual resultPass criteria
First playPlaybackFoundationTest.fetchTicketUsesAuthenticatedTicketRouteAndBuildsTicketedStreamUrl and ready-state testsBlockedBlockedDetail/cache entry starts playback without exposing session tokens.
ResumeserverResumeProgressLoadsBeforeTicketedPlaybackWhenNoExplicitStartWasChosenBlockedBlockedResume starts from server watch position.
Start overStart-over skip-resume tests in PlaybackFoundationTestBlockedBlockedPlayback starts at 0s and does not reuse stale progress.
PauseProgress-write tests in PlaybackFoundationTestBlockedBlockedPause keeps UI recoverable and commits current progress.
Exit / BackExit progress write tests and TvPlaybackOverlayReducerTestBlockedBlockedBack/exit returns to detail/home, saves progress when duration is known, and avoids app-data wipe.
CompletiononPlaybackEnded progress testBlockedBlockedCompletion commits ended position and returns to usable detail/home state.
Progress refreshProgress commit callbacks and watch-state tests in full unit gateBlockedBlockedDetail/home watch badges update after progress or watched/unwatched mutation.
Ticket/session expiryTicket 401/403 retry/invalidation and resume/progress auth-failure testsBlockedBlockedExpiry retries within limits, then returns to sign-in/change-server recovery instead of trapping the player.
Server unreachableTicket network retry-limit testsBlockedBlockedShows retry/change-server/sign-out recovery after bounded retries.
Missing file / offline library404/503 playback failure testsBlockedBlockedShows retry/change-server/sign-out recovery and does not clear app data.
Audio track casesPlaybackTrackOptionsTest.audioOptionsFormatLabelsLanguagesAndCapabilityWarningsAcrossGroupsBlockedBlockedMultiple/unsupported audio streams show labels, support warnings, and safe selectable/disabled states.
Subtitle casesPlaybackTrackOptionsTest.subtitleOptionsAlwaysOfferOffAndFormatRolesAndFallbacks and subtitleOffIsSelectedWhenTextDisabledOrNoTextTracksExistBlockedBlockedSubtitles offer Off, selectable text tracks can be enabled, and empty/unsupported tracks remain graceful.
Track diagnosticsPlaybackTrackOptionsTest.diagnosticsSummarizeTrackGroupsWithoutUrlsBlockedBlockedDiagnostics summarize track groups without URLs or playback tickets.

Source matrix: Android TV 10-foot QA matrix. Manual TV D-pad execution remains blocked by the absent TV emulator/device; source/unit evidence listed below passed in the full Android unit gate.

Surface / scenarioAutomated/source evidenceManual TV resultPass criteria
Server / login / recoveryTvAuthRecoveryPolicyTest; TvDiagnosticsScreen and TV recovery panels share TvActionPanel focus restoration.BlockedInitial focus is deterministic; Retry, Sign out, Change server, Reset connection, Back, and diagnostics remain reachable.
HomeTvHomeFocusPolicy, TvFocusRestoreModelTest, and TV home source expose Settings & Diagnostics and recovery panels.BlockedD-pad traverses actions, shelves, library chooser, rows/grids, and recovery controls with visible focus.
Library gridsTV grid/detail source plus full build/lint.BlockedGrid cards, filters/sorts where available, retry/cache-clear/change/reset actions, and Back-to-Home behavior remain reachable.
SearchTV search rows and cache-miss diagnostics actions; full build/lint.BlockedSearch field, retry, clear, result rows, cache-miss retry, diagnostics, and Back-to-Home are reachable.
DetailTV detail source plus playback/watch-state tests.BlockedDetail starts with safe Back focus, launches Resume/Start over, exposes watch actions, recovery, diagnostics, and origin restore.
Player controlsTvPlaybackOverlayReducerTest and playback source.BlockedHidden controls show on D-pad, OK toggles, Back first restores controls then exits, and recovery actions appear on errors.
TracksPlaybackTrackOptionsTest and TvPlaybackOverlayReducerTest.BlockedAudio/subtitle pickers have bounded D-pad traversal, selected/disabled states, Back dismissal, and safe focus restoration.
Dialogs / confirmationsDiagnostics clear confirmation and TV action panel source.BlockedConfirmation panels keep focus in reachable actions and always include cancel/back.
ErrorsRecovery panels in TV auth/home/grid/detail/search/player source.BlockedError panels focus the first useful recovery action and never require OS app-data wipe.
Back behaviorTvAuthRecoveryPolicyTest, TvFocusRestoreModelTest, TvPlaybackOverlayReducerTest.BlockedAuth/recovery consumes Back, grid/search/detail return to parent, player uses two-step Back, Home uses system Back.
Post-reset navigationTV auth/recovery source and 10-foot matrix.BlockedReset returns to server URL field, login/Home focus recovers after reconnect, and reset can be repeated.
Diagnostics screenTvDiagnosticsScreen, DiagnosticsUiModelsTest, focused diagnostics tests.BlockedDiagnostics action panel autofocuses Export/Clear/Back, share failures are recoverable, clear confirmation preserves non-diagnostic state.
  • Physical Android TV / remote run: unavailable in this workspace; release-readiness follow-up before promoting Android TV beyond dev.
  • Phone emulator / physical phone run: unavailable in this workspace; release-readiness follow-up before claiming manual phone UX evidence.
  • Android TV emulator run: unavailable because no TV system image/AVD is configured and the SDK emulator binary fails under NixOS stub-ld in this workspace.
  • Screenshots / video / logcat captures: not obtained because no device/emulator was attached. Capture and redact evidence using the guidance below.
  • External subtitle discovery: out of scope for this QA packet. Current evidence covers Media3 subtitle track selection/toggle once tracks are present, not discovery of sidecar/external subtitle files.
  • Release-readiness media fixtures: hardware playback still needs real media fixtures for unsupported/exceeds-capability audio, subtitles, missing-file/offline-library recovery, and server expiry cases.

Screenshot, video, logcat, and export redaction guidance

Section titled “Screenshot, video, logcat, and export redaction guidance”

When a phone, emulator, or TV is available, record the target details before capturing evidence:

Terminal window
adb devices -l
adb shell getprop ro.build.version.sdk
adb shell getprop ro.build.version.release
adb shell getprop ro.product.model
adb shell wm size

Useful capture commands:

Terminal window
# Screenshot
adb exec-out screencap -p > ferrex-android-qa-screenshot.png
# Short video; stop with Ctrl-C or after the scenario completes.
adb shell screenrecord /sdcard/ferrex-android-qa.mp4
adb pull /sdcard/ferrex-android-qa.mp4 ./ferrex-android-qa.mp4
adb shell rm /sdcard/ferrex-android-qa.mp4
# Logcat snapshot
adb logcat -c
# Reproduce the scenario, then:
adb logcat -d -v threadtime > ferrex-android-qa-logcat.txt
# Optional full bugreport, only if the reviewer requests it.
adb bugreport ferrex-android-qa-bugreport.zip

Before attaching any screenshot, video, logcat, bugreport, or diagnostics export, redact or replace:

  • Server URLs when they are sensitive, including hostnames, base paths, LAN IPs, and reverse-proxy paths.
  • Usernames, display names, avatars, account names, and any other identifying profile data.
  • Authorization, Proxy-Authorization, cookie, bearer/basic auth, refresh/access token, API key, session, device-session, local-device, and account/device identifier values.
  • Playback ticket URLs and query parameters such as access_token, ticket, device_session_id, and stream IDs when they identify private media.
  • Passwords, PINs, PIN proofs, setup tokens, private keys, signatures, and registration/private material.
  • Local filesystem paths if they reveal private usernames, library names, or hostnames.

For exported diagnostics zips, unpack into a scratch directory and spot-check before attaching:

Terminal window
mkdir -p /tmp/ferrex-diagnostics-review
unzip ferrex-diagnostics-*.zip -d /tmp/ferrex-diagnostics-review
rg -n "Authorization|Bearer|refresh_token|access_token|password|pin|ticket|session|device|private_key|BEGIN PRIVATE" /tmp/ferrex-diagnostics-review

The expected result is either no matches or matches whose values are <redacted> / hashed safe summaries.

  • Android playback QA matrix — existing playback/watch-state matrix incorporated into the final packet.
  • Android TV 10-foot QA matrix — existing 10-foot focus matrix incorporated into the final packet.
  • mobile/android/app/src/main/kotlin/com/ferrex/android/core/diagnostics/AndroidDiagnostics.kt
  • mobile/android/app/src/main/kotlin/com/ferrex/android/core/diagnostics/DiagnosticsExportBuilder.kt
  • mobile/android/app/src/main/kotlin/com/ferrex/android/core/diagnostics/DiagnosticsExportShare.kt
  • mobile/android/app/src/main/kotlin/com/ferrex/android/core/diagnostics/DiagnosticsFiles.kt
  • mobile/android/app/src/main/kotlin/com/ferrex/android/core/diagnostics/DiagnosticsRedactor.kt
  • mobile/android/app/src/main/kotlin/com/ferrex/android/core/diagnostics/DiagnosticsUiModels.kt
  • mobile/android/app/src/main/kotlin/com/ferrex/android/ui/diagnostics/PhoneDiagnosticsScreen.kt
  • mobile/android/app/src/tv/kotlin/com/ferrex/android/tv/ui/TvDiagnosticsScreen.kt
  • mobile/android/app/src/main/kotlin/com/ferrex/android/ui/home/PhoneHomeScreen.kt
  • mobile/android/app/src/main/kotlin/com/ferrex/android/ui/recovery/PhoneRecoveryScreens.kt
  • mobile/android/app/src/tv/kotlin/com/ferrex/android/tv/ui/TvHomeScreen.kt
  • mobile/android/app/src/main/kotlin/com/ferrex/android/ui/player/PlayerScreen.kt
  • mobile/android/app/src/test/kotlin/com/ferrex/android/core/diagnostics/DiagnosticsCoreTest.kt
  • mobile/android/app/src/test/kotlin/com/ferrex/android/core/diagnostics/DiagnosticsUiModelsTest.kt
  • mobile/android/app/src/test/kotlin/com/ferrex/android/core/playback/PlaybackFoundationTest.kt
  • mobile/android/app/src/test/kotlin/com/ferrex/android/core/playback/PlaybackTrackOptionsTest.kt
  • mobile/android/app/src/test/kotlin/com/ferrex/android/core/playback/TvPlaybackOverlayReducerTest.kt
  • mobile/android/app/src/test/kotlin/com/ferrex/android/core/tvfocus/TvAuthRecoveryPolicyTest.kt
  • mobile/android/app/src/test/kotlin/com/ferrex/android/core/tvfocus/TvFocusRestoreModelTest.kt
  • mobile/android/app/build.gradle.kts and Android manifests for package/flavor/launcher details.

Intake / historical reference files consulted

Section titled “Intake / historical reference files consulted”
  • feat/android@intake:docs/specs/mobile-apps-android.md — Android architecture/build/codegen baseline reference.
  • feat/android@intake:docs/specs/android-tv-spike.md — TV product-flavor and Leanback shell reference.
  • feat/android@intake:mobile/android/app/src/main/kotlin/com/ferrex/android/core/diagnostics/DiagnosticLog.kt — historical diagnostics log reference only; current diagnostics implementation was reviewed from the active workspace files above.
  • symphony/issue/LOW-149:docs/specs/android-tv-dev-acceptance.md — historical Android/TV dev acceptance checklist used to recreate criteria here as fresh documentation.

LOW-154 and LOW-149 were treated as historical references only. No LOW-154/LOW-149 artifact was revived, edited, or used to mutate Linear issue state.