I have an app that can stream local media content to a Chromecast receiver. This mostly works, except that when the device is asleep and not on external power the session will die/disconnect after about 5 minutes (measured from when the screen goes blank).
I've already had a look at this question here:
How do I keep a ChromeCast route alive when my app is in the background on battery power?
...and implemented both of the suggested answers.
Specifically, in my app's manifest, I have:
<uses-permission android:name="android.permission.WAKE_LOCK" />
And then in the embedded HTTP server that I use to stream media content to the Chromecast receiver, I'm doing:
PowerManager powerManager = (PowerManager)Environment.getApplicationContext().getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cast-server-cpu");
wakeLock.acquire();
WifiManager wifiManager = (WifiManager)Environment.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "cast-server-net");
wifiLock.acquire();
That code executes when the server thread starts (occurs when the Chromecast route is selected), and the locks aren't released until the server is stopped. That happens in my MediaRouter.Callback
implementation, which (incorporating the accepted answer from the question above) looks like this:
this.mediaRouterCallback = new MediaRouter.Callback() {
private MediaRouter.RouteInfo autoconnectRoute = null;
@Override
public void onRouteSelected(MediaRouter mediaRouter, MediaRouter.RouteInfo routeInfo) {
if (autoconnectRoute == null) {
if (isPlaying()) {
stop();
playButton.setImageResource(R.drawable.icon_play);
}
castDevice = CastDevice.getFromBundle(routeInfo.getExtras());
if (castServer != null) {
castServer.stop();
}
//start the Chromecast file server
castServer = new CastHttpFileServer();
new Thread(castServer).start();
}
initCastClientListener();
initRemoteMediaPlayer();
launchReceiver();
}
@Override
public void onRouteUnselected(MediaRouter mediaRouter, MediaRouter.RouteInfo routeInfo) {
if (! doesRouterContainRoute(mediaRouter, routeInfo)) {
//XXX: do not disconnect in this case (the unselect was not initiated by a user action); see https://stackoverflow.com/questions/18967879/how-do-i-keep-a-chromecast-route-alive-when-my-app-is-in-the-background-on-batte
autoconnectRoute = routeInfo;
return;
}
//user disconnected, teardown and stop playback
if (isPlaying()) {
stop();
playButton.setImageResource(R.drawable.icon_play);
}
if (castServer != null) {
castServer.stop();
castServer = null;
}
teardown();
castDevice = null;
}
@Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
super.onRouteAdded(router, route);
if (autoconnectRoute != null && route.getId().equals(autoconnectRoute.getId())) {
this.onRouteSelected(router, route);
}
}
private boolean doesRouterContainRoute(MediaRouter router, MediaRouter.RouteInfo route) {
if (router == null || route == null) {
return false;
}
for (MediaRouter.RouteInfo info : router.getRoutes()) {
if (info.getId().equals(route.getId())) {
return true;
}
}
return false;
}
};
What more needs to be done in order to keep the Chromecast session alive when the device is asleep and not connected to external power?
Also, considering that this issue only reproduces when the device is not connected to USB, what's an effective way to debug in this case?
Edit
Here's the stacktrace that's reported when the session dies (from the embedded HTTP server):
java.net.SocketException: Software caused connection abort
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:112)
at java.net.SocketOutputStream.write(SocketOutputStream.java:157)
at com.myapp.CastHttpFileServer.writeResponse(CastHttpFileServer.java:124)
at com.myapp.CastHttpFileServer.run(CastHttpFileServer.java:180)
And I've also noticed that I can capture this event in my RemoteMediaPlayer.OnStatusUpdatedListener
implementation, like:
if (lastStatus == MediaStatus.PLAYER_STATE_PLAYING && mediaStatus.getPlayerState() == MediaStatus.PLAYER_STATE_BUFFERING) {
//buffer underrun/receiver went away because the wifi died; how do we fix this?
}