Upstream pr/tvos support - #226
Open
andrew-harness wants to merge 6 commits into
Open
Conversation
Entry names were built with to_string_lossy on a relative path, so on Windows they carried backslashes. Zip requires '/', and readers split on it to locate a bundle, so an archive exported on Windows could not be read back by tooling that looks for Payload/<App>.app/Info.plist. Join the path components rather than replacing separators, so a name containing a literal backslash on a platform that permits one survives.
The pinned release sent one segment and waited for its acknowledgement before sending the next, so any transfer over the userspace TCP stack ran at one segment per round trip regardless of the link. 0.1.6 keeps a send window bounded by the peer's advertised window, with window scaling. Measured over a CoreDevice tunnel: 2.3 MB/s to 6.35 MB/s, no retransmissions.
An Apple TV is reached over the network rather than usbmuxd, so it has to be found before anything else can talk to it. Adds a discovery module with a backend per platform. mdns-sd serves macOS and Linux. It does not work on Windows: it binds its own socket on port 5353 and never receives the device's responses, because the Dnscache service owns multicast DNS there, so Windows uses the OS resolver through dnsapi.dll instead. Browsing alone returns SRV records only intermittently, so each instance is resolved individually to obtain a port. Apple TVs advertise two RPPairing services that are not interchangeable. _remotepairing-manual-pairing._tcp is advertised only while the device is showing a pairing code and is the only one that accepts first-time pairing; _remotepairing._tcp is advertised once a pairing exists and only accepts reconnection. They carry different ports, and the identifier they advertise changes between advertisements, so it cannot be used as an identity. Devices are grouped by hostname instead, and a companion-link advertisement supplies the model a paired device otherwise stops publishing.
Pairing runs the SRP exchange over RPPairing. The device displays its code only after accepting the request and stops displaying it when the session closes, so pairing takes a provider rather than a code and invokes it while the session is held open. Installing establishes a TLS-PSK tunnel, runs the CoreDevice and RSD handshakes, and then uses the same installation entry point USB does. Reconnection is verify-only and never falls back to pair-setup, so reaching a tunnel cannot make a device display a code to someone who did not ask to pair. The MSS is taken from the MTU the tunnel just negotiated. The stack behind the tunnel sends one segment per round trip, so the segment size sets the transfer rate outright; left unset it falls back to a default an order of magnitude below what the tunnel carries. mDNS never advertises a UDID, so the real one is read from the RSD handshake. It is kept separate from the key the pairing file is stored under, which leaves udid empty until a tunnel supplies it rather than letting a fabricated value reach Apple.
Signing for an Apple TV failed with error 8220, "your team has no devices from which to generate a provisioning profile", even with the device registered and listDevices returning it as tvOS. The portal defaults every request to iOS, so it looked for an iOS device and found none. Carry the platform in the request body as DTDK_Platform and subPlatform. The URL is not the mechanism: the /tvos/ path segment does not exist on this API version and probing it returns a non-plist body. A/B tested against the live API with one variable changed, downloadTeamProvisioningProfile returns 8220 without these fields and a profile with them. iOS sends no extra fields, so its requests stay byte-identical. Certificates and app groups are deliberately left unparameterised; a certificate issues correctly for a tvOS profile without them. Refreshing also refuses to register a device whose UDID is still unknown, which a network device's is until a tunnel supplies it.
Adds a pairing screen reached from the main screen: scan, pick a device, enter the code it displays, pair. Once paired, an Apple TV appears in the same device picker USB devices use, so installing to one goes through the normal flow rather than a separate path. Network devices are rescanned on a timer and only reported gone after two consecutive misses, because a single missed response is common and would otherwise move the user's device selection without them noticing. Over a tunnel the bundle is uploaded as one archive rather than mirrored file by file. AFC costs a round trip per file open, write and close, and a tunnelled round trip is dear enough that hundreds of small files cost far more than the bytes in them; measured on a 66 MB bundle of 444 files, that overhead was most of the transfer. usbmuxd round trips are cheap by comparison, so it keeps mirroring there, where compressing would cost more than it saves. The bar no longer claims a percentage for the upload, which reports none. It names the size being sent instead, so a transfer that takes a while does not read as a hang.
5 tasks
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.
Implements #155, sideloading to an Apple TV over the network.
An Apple TV has no USB port, so it's reached over the network using Apple's own
RemotePairing/CoreDevice path: mDNS discovery, SRP pairing against the PIN shown on screen, a
TLS-PSK tunnel, RSD service discovery, then the same InstallationProxy install the USB path
already uses.
Verified on hardware
Apple TV 14,1 on tvOS 26.5, driven from Windows. Discovery, first-time pairing, reconnect, tunnel,
device registration, provisioning, signing, upload and install. The installed app shows up on the
home screen and launches and runs, which is the part that matters: a wrongly signed or wrongly
provisioned bundle installs fine and then refuses to launch.
Commits
Six commits, each one is a self-contained layer:
fix: write zip entry names with forward slasheschore(deps): update jktcp for windowed TCP sendsfeat: discover Apple TVs over mDNSfeat: pair with an Apple TV and install over a network tunnelfeat: request tvOS provisioning from the developer portalfeat: pair, select and install to Apple TVs from the GUII'm happy to split this into separate PRs if you'd rather review it that way.
Two things worth pointing out
The zip fix isn't tvOS related.
archive_package_bundlebuilt entry names withto_string_lossyon a relative path, so on Windows they carried backslashes. Zip requires
/, and readers split onit to find the bundle, so IPAs exported on Windows were malformed. It's first in the series so it
can be taken on its own.
tvOS provisioning is selected in the request body, not the URL. The portal defaults every request
to iOS and returns error 8220 for an Apple TV even with the device registered and
listDevicesreturning it as tvOS. Sending
DTDK_Platform=tvosandsubPlatform=tvOSfixes it. The endpointURLs are unchanged: a
/tvos/path segment returns a non-plist body on this API version, so theplatform has to travel in the body. A/B tested against the live API with one variable changed. iOS
requests are unchanged byte for byte.
Performance
A network install of a 66 MB bundle took about three minutes at first, and is now about eight
seconds:
tunnel was most of the transfer time. A network install now uploads a single archive. usbmuxd
round trips are cheap, so it still mirrors the directory there.
jktcpsent one segment per round trip; 0.1.6 keeps a real send window