Repository navigation
Conversation
The remote's Subtitles menu listed tracks, eleven delay offsets and three positions. It now lists Off and the subtitle tracks only, the same as silo-apple. Delay and position stay in the TV's own player menu, which is where they are adjusted while watching. The dropdown no longer needs section headers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 of 3 tasks
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Quick104
added a commit
that referenced
this pull request
Oct 21, 2026
…#653) * test(cast): add adb hooks to inspect and inject Remote Control targets Emulators sit behind NAT that mDNS can't cross, so a phone emulator can't discover a TV emulator or an Apple TV simulator, and there was no way to see what a TV advertises. Debug builds get two more PlaybackDebugReceiver actions: - CAST_STATUS (TV): the live Remote Control port, name and TXT record. - CAST_TARGET (phone): adds or replaces a TV target from a host, port, name and TXT string resolved on the host; `--ez clear true` removes them. Injected targets merge with real discovery, survive a restart of it, and win over an mDNS record with the same device id. The browser's TXT parsing moves into SiloCastNsdBrowser.targetFromRecord so the hook and discovery read records the same way, and the advertiser keeps the record it registered so the hook reports exactly that. Behavior of release builds is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cast): let the phone share a TV with other controllers Apple TVs now keep several Remote Control controllers connected and say why they close a session; Android phones treated every close as "someone took over", forgot the TV, and replaced other profiles' titles without asking. This brings the phone and the shared wire format level with silo-apple's SiloControl contract. Every addition is optional and feature-gated, so older peers see today's behavior. Wire (shared): - hello: serverIdentity, features, passive, clientKind; `presence` message; optional `close` reason, decoded leniently as Apple does. - handoff_offer: serverIdentity, resumeToken, replaceConfirmed, replacingContentId. handoff_ready: resumeToken. handoff_cancel: nowPlaying. - Feature, close-reason, cancel-reason and client-kind names, matching Apple's strings. - TVs advertising serverIdentity match an active server on the same deployment at another address. Phone: - Lists close_reason, resume_token and replace_confirmation; a silent auto-resume connects passive and sends presence once the user engages (remote opened, a command, or a title sent). - Close policy: an older TV's close still forgets it. A multi-controller TV's timeout reconnects; tv_disconnected forgets the TV; other reasons end the session but keep the target, and the remote keeps the TV's error. - Sends its profile name, its verified server identity, and the resume token the TV issued it; the token lives in memory per TV, server and profile. - "Replace what's playing?" on the remote, for a TV's replace_confirmation_required and before sending a different title to a TV that is playing one. The consent names the title the user saw. - Play and pause buttons send explicit play/pause for what they show, never play_pause. A rate_limited reply no longer fails a launch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(tv): keep several Remote Control controllers connected to one TV An Android TV kept one controller: every new TCP connection, even a port probe, closed the phone that was using it, so two people on a couch, or a phone and a Home Assistant integration, kept knocking each other off. Apple TVs now keep several controllers; this brings the Android TV receiver to the same contract (silo-apple SiloControlReceiverPolicy), with the pure rules in TvSiloCastReceiverPolicy. - Up to 8 controllers, each with its own heartbeat, authorization and command budget (20/s, burst 40, `rate_limited`). At the cap the TV closes, in order, a connection that never said hello, an idle cross-server controller, then the oldest passive one, never one that is handing off or in use; otherwise it refuses the newcomer with `too_many_controllers`. - A new connection from the same device replaces its older sockets. - One state tick goes to every authorized controller, through a latest-only slot per controller so a slow peer holds up no one; errors go only to the controller that caused them. - One handoff at a time: a newer offer from another controller cancels the pending one with `superseded`. A launch plays under a borrowed profile only for the controller whose handoff produced it, and reusing that profile needs the same connection or the resume token issued to it (device IDs are self-reported). Keeping it alive while its phone reconnects still follows that phone. - A handoff that would replace a title another profile is watching asks first (`replace_confirmation_required` with what is playing), when the controller can ask, and checks again just before it stops playback. - Passive controllers never put up standby; standby names the newest waiting controller and counts the rest. Disconnect on the TV closes the non-passive ones with `tv_disconnected`. - After the borrowed profile changes, every controller is re-checked; one that loses authorization is told `server_mismatch`, unless its own profile simply ended, and cross-server ones stay connected to hand off again. - A borrowed profile whose session the server stops refreshing now ends: every authorized controller gets `temporary_session_expired`, playback under it stops, and the TV falls back to its own sign-in. The token manager reports this through a new `temporarySessionExpired` flow. - Close reasons go to controllers that list `close_reason`; the TV advertises `serverIdentity` in hello and Bonjour and matches controllers on it as well as the server ID. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cast): keep injected targets authoritative and report only a finished registration Review follow-ups on the debug hooks: - A resolved mDNS record could pass the "no injected target with this id" check outside the browser's lock and then overwrite an injected target set in between. Every write to the target list now holds the lock. - Clearing an injected target dropped the discovered record it had replaced until discovery found it again. The browser keeps the hidden record and restores it. - CAST_STATUS reported the requested advertisement before NsdManager confirmed it, and under the requested name. It now reports only a registered advertisement, under the name NsdManager registered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cast): learn the server's identity when Remote Control starts A server signed in before the identity contract has no verified identity on the phone. The picker then can't list a TV that advertises another address of the same deployment, and hello and the handoff offer can't name the deployment. silo-apple learns it on the server's next activation; the phone now learns it once when it starts looking for TVs (the picker or a foreground auto-resume), through ServerIdentityRepository. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(tv): fence state and handoff rollback to the right controller Review follow-ups in the multi-controller receiver: - broadcastState chose its recipients before reading the player state, so a handoff that changed the TV's profile during the read could send the new profile's state to a controller it had just de-authorized. It now chooses recipients after the read, and each controller's writer checks authorization again before sending a queued state. - A failed handoff_ready rolled back the session's current readiness. A newer offer from the same phone could own it by then, so the older attempt's failure ended the newer profile. Each attempt now rolls back only the generation it granted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(tv): grant readiness only for the prepared profile, and don't let expiry wait on writes Review follow-ups in the multi-controller receiver: - completeHandoff granted readiness for whichever profile was active. It now checks, under the receiver lock, that the active profile is the one this offer prepared, and fails the handoff otherwise. - When the server refused a borrowed profile's refresh, the TV notified every controller before stopping playback and ending the profile, so a controller that stopped reading could stall both. It now tears down first and sends the notices in the background. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(tv): check state authorization when the write is admitted A queued state was checked against the controller's authorization before the writer waited for the connection's write lock, and wasn't tied to the profile it was read under. Each state now carries the borrowed-profile generation it was read under, and the writer checks authorization and that generation after taking the write lock, just before writing. A state read under another profile, or queued before the controller lost access, is dropped; the next tick follows within half a second. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cast): show only subtitle languages in the phone remote (#654) The remote's Subtitles menu listed tracks, eleven delay offsets and three positions. It now lists Off and the subtitle tracks only, the same as silo-apple. Delay and position stay in the TV's own player menu, which is where they are adjusted while watching. The dropdown no longer needs section headers. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Related issue: N/A
Validation tasks: none
The phone remote's Subtitles menu listed the tracks, eleven delay offsets and three positions. The iPhone showed the same items in a different order, so the two phone apps disagreed, and the languages were buried among timing options.
The Subtitles menu now lists Off and the subtitle tracks, the same as silo-apple.
Approach
The matching Apple change is Silo-Server/silo-apple#860. Stacked on #653.
Validation
:androidApp:testDebugUnitTest,lintDebugandlintVitalRelease: pass.Evidence
Evidence: https://evidence.siloserver.org/r/silo-android/pr-654/
Surface and build: Android phone emulator, before on #653 (
12ff75145), after on this branch. The tablet and foldable sizes are the same signed-in emulator resized withwm sizeandwm density. The Android TV app doesn't change.Risks
None identified. Subtitle delay and position remain available on the TV.
Checklist
AI Disclosure
🤖 Generated with Claude Code
Note
Show Subtitles chip in cast remote only when subtitle tracks exist
Simplifies the phone remote subtitle menu in SiloCastRemoteScreen.kt to show only subtitle language choices.
RemoteChipMenuno longer renders section headers; every entry is a selectable dropdown item. TheMenuEntry.isSectionHeaderfield and thesubtitleDelayLabelhelper were deleted.subtitleMenuEntriesandRemoteSecondaryControlsfor the removed callbacks.Macroscope summarized ff88c5b.