简体   繁体   中英

How to keep a ChromeCast session alive when the device is asleep?

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?
}

CommonsWare had the right of it in his comment. I was testing on a device running Android 7.1.2, and Doze mode was causing the network to drop after about 5 minutes of inactivity and irrespective of the wake lock(s) my app was holding .

The fix was to add the following permission in the manifest:

<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

And then to update my onRouteSelected() callback implementation to request that Doze mode be disabled if/when a ChromeCast route is selected:

if (Build.VERSION.SDK_INT >= 23) {
    String packageName = context.getPackageName();
    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    if (! pm.isIgnoringBatteryOptimizations(packageName)) {
        //reguest that Doze mode be disabled
        Intent intent = new Intent();
        intent.setAction(
            Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
        intent.setData(Uri.parse("package:" + packageName));

        context.startActivity(intent);
    }

}

As long as the user selects 'Yes' when prompted, the app's wake locks are respected and the ChromeCast session stays alive even when the device is not charging.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM