Cast: Implement connectionless (Cast.API_CXLESS) device controller#3570
Open
peterhel wants to merge 2 commits into
Open
Cast: Implement connectionless (Cast.API_CXLESS) device controller#3570peterhel wants to merge 2 commits into
peterhel wants to merge 2 commits into
Conversation
CastContextImpl never registered a route-selection callback, so selecting a Chromecast via the Cast SDK (CastContext/SessionManager) did nothing: discovery worked but no session was ever started and the app's SessionManagerListener never fired. MediaRouterCallbackImpl already had the correct start-on-selection logic but was never instantiated or registered with the router. Register MediaRouterCallbackImpl via IMediaRouter (registerMediaRouterCallbackImpl + addCallback) so route selection starts a session. This goes through the IMediaRouter binder rather than touching androidx MediaRouter from the dynamite, which would throw Resources$NotFoundException (the dynamite uses the app's Resources but microG's resource IDs). Also resolve defaultSessionProvider by category prefix: the provider map is keyed by the full control category (with namespace/flag suffixes such as .../CC1AD845///ALLOW_IPV6), so the exact categoryForCast(appId) lookup missed and left defaultSessionProvider null. Harden MediaRouterCallbackImpl.onRouteSelected against a missing provider / non-SessionImpl and fall back to the route extras. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Modern Cast SDK clients — and apps such as Amazon Prime Video — drive the Cast device over the connectionless API instead of the classic connection-based path. They bind the existing device-controller service, but unlike the classic client they deliver their listener out-of-band via setListener() and then call connect(), and they wait for the service to reply ICastDeviceControllerListener.onConnectedWithResult before launching an application. microG only implemented the classic path, so these clients bound the service and then stalled forever; withholding the cxless features instead made the SDK report microG as too old (ConnectionResult=2). Add the missing connectionless surface: - ICastDeviceController: connect() (txn 17), setListener() (18) and unregisterListener() (19). The other transaction numbers already match the framework; these methods were simply absent. - ICastDeviceControllerListener: onConnectedWithResult() (txn 14), the readiness signal the connectionless client blocks on. - CastDeviceControllerImpl: open the CastV2 channel on connect() and emit onConnectedWithResult(SUCCESS); store the listener delivered via setListener(); report onSendMessageSuccess from the outgoing send keyed by its requestId (reporting it from inbound messages completed a non-existent client task and threw a RemoteException); and disconnect from the device if the client process dies (linkToDeath) so the connection and its reader thread are not leaked. - CastDeviceControllerService: advertise the requested API features via ConnectionInfo so the client's availability check passes. Tested on-device: the real Amazon Prime Video app launches its own receiver and plays a DRM title end to end through this path. Co-Authored-By: Claude Opus 4.8 <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.
Cast: Implement connectionless (Cast.API_CXLESS) device controller
Summary
Modern Cast SDK clients — and real apps such as Amazon Prime Video, YouTube,
Netflix — drive the Cast device over the connectionless Cast API
(
Cast.API_CXLESS) rather than the classic connection-based path that microGimplemented. As a result, casting from these apps never worked on microG: the
session would start and then stall indefinitely.
This PR implements the connectionless device-controller surface. With it, the
real Amazon Prime Video app launches its own receiver and plays a DRM title
end to end on a Chromecast, cast from a de-Googled phone.
Builds on #3567 (which makes the framework start a session when a Cast route is
selected); that session is the trigger that binds the device controller this PR
completes.
The problem
The connectionless client binds the same
ICastDeviceControllerservice, butits handshake differs from the classic client:
GetServiceRequest"listener"extra and connects lazily on the first launch/sendMessage.
setListener(), then callsconnect(), and waits for the service to callICastDeviceControllerListener.onConnectedWithResultbefore it will launch anapplication. It is otherwise purely reactive.
microG implemented only the classic path. So a connectionless client bound the
service, called
setListener/connect(transactions microG didn't handle, andwhich were silently dropped because they are
oneway), never receivedonConnectedWithResult, and stayed "not connected" forever. Withholding thecxless_*features to force the classic path instead just made the SDK decidemicroG was too old and fail with
ConnectionResult=2.The fix
The transaction numbers in microG's AIDL already matched the framework — the
connectionless methods were simply absent. The change is additive:
ICastDeviceController.aidl— addconnect()(txn 17),setListener(ICastDeviceControllerListener)(18),unregisterListener()(19).ICastDeviceControllerListener.aidl— addonConnectedWithResult(int)(txn 14), the readiness signal.
CastDeviceControllerImpl.java:connect()opens the CastV2 channel and emitsonConnectedWithResult(SUCCESS).setListener()stores the out-of-band listener.onSendMessageSuccessis now reported from the outgoingsendMessage,keyed by that send's
requestId. Previously it was reported from inboundmessages using the response's id, which completed a non-existent client task
and threw a
RemoteException. Inbound messages now go solely throughonTextMessageReceived.linkToDeath; if the clientprocess dies without a clean
disconnect(), the device connection (and itsreader thread) is torn down instead of leaked.
CastDeviceControllerService.java— advertise the requested API featuresvia
ConnectionInfoso the client's availability check passes.Connectionless handshake (for reference)
Testing
Validated on a Pixel 2 (LineageOS-for-microG, Android 15) against a Chromecast:
launches Prime's own receiver (appId
17608BC8), Prime completes itsproprietary registration/settings handshake on
urn:x-cast:com.amazon.primevideo.cast, LOADs a DRM title, and the titleplays on the TV — with zero failed listener callbacks.
(
playerState=PLAYING, currentTime advancing).linkToDeathcleanup(device disconnected, no leaked connection).
Notes / scope
CastMediaRouteController.onSelectremains a stub; it is a separate legacyroute-control path and does not gate the connectionless flow (the framework's
session manager binds the device controller directly).
CAST_APIservice (id 161 — settings/relay/logging) is stillunimplemented; it is not on the media path and its absence did not affect
casting in testing.