Disconnect on Inactivity for Roku Apps

Roku with You.i Roku Cloud apps are almost inactive while the Roku client is playing a video, or when the end user isn’t using the Roku remote. During this time, Cloud server instances can be made available to other Roku clients by saving their state, which can be used by the new server instances to resume the application. This allows a greater number of users to be serviced by the Cloud infrastructure while reducing costs and the number of server instances running at a given time. We call this feature “Disconnect on Inactivity”.

To support this feature in your app, you need to add app logic to save and restore the app’s state at the appropriate times.

How Disconnect on Inactivity Works

When a Roku client is inactive for more than the noActivitySessionTimeoutSec time, the You.i Roku Cloud infrastructure disconnects the Roku client from the Cloud server by providing it with the saved state information from the previous connection. The client reconnects again with the server after the end-user’s interaction with the app.

The You.i Roku Cloud server saves its state with the CYICloud::GetInterface().SaveInstanceState(savedState) API for a You.i C++ app, in response to the screenSaverWillStart event. The Cloud server instance is destroyed once the saveInstanceState is complete. For more information on the APIs, see saveInstanceState() and getSavedInstanceState().

The saved state is a deep link to the screen at the point when the client became inactive. A stack of screen data could also be stored to restore the history. The app state is stored by the Roku client as a JSON stringified blob.

Sequence of Events Between Client, Infrastructure, and Server

The following sequence diagram illustrates the sequence of events between the Roku client, Cloud infrastructure, and Cloud server when the screensaver is active for longer than noActivitySessionTimeoutSec.

Sequence diagram of a scenario when the screensaver is active for longer than noActivitySessionTimeoutSec

The following sequence diagram illustrates the sequence of events between the Roku client, Cloud infrastructure, and Cloud server when the screensaver is active for less than noActivitySessionTimeoutSec, but the server exceeds two ping durations (which is every 10 seconds) from the client.

Sequence diagram of a scenario when the screensaver is active for less than noActivitySessionTimeoutSec

Server Activity Cycle for Disconnect on Inactivity

The following diagram explains the You.i Roku Cloud server activity cycle for Disconnect on Inactivity:

You.i Roku Cloud server activity cycle for disconnect on inactivity

Enabling Disconnect on Inactivity

To enable the Disconnect on Inactivity feature, you need to update the client configuration file and subscribe to events from the client.

Edit the Client Configuration File

Add the following to clientConfig.json, located at ../youi/build/<platform>/<configuration>/assets/json/default/:

  "inactivityDisconnect": true

Setting inactivityDisconnect to true causes the application to save its state when the client is inactive.

The client sends the screenSaverWillStart event 30 seconds before the screensaver actually starts, which enables the server to send the app state data to the client. If the screensaver active time is longer than noActivitySessionTimeoutSec, which is configured by the Cloud infrastructure, the connection to the server instance is terminated. On an end-user’s interaction with the app, the connection between client and new server instance is established, and the client starts from the saved state, instead of the beginning.

Subscribe to Events from the Client

Subscribe to events from the client on UserStart as shown in the following example.

bool VideoPlayerApp::UserStart()
  // ...
  CYICloud::GetInterface().ClientBackgrounded.Connect(*this, &VideoPlayerApp::OnClientBackgrounded, EYIConnectionType::Async);
  CYICloud::GetInterface().ScreenSaverWillStart.Connect(*this, &VideoPlayerApp::OnScreenSaverWillStart, EYIConnectionType::Async);
  // ...

void VideoPlayerApp::OnClientBackgrounded()
  YI_LOGI(LOG_TAG, "OnClientBackgrounded.");

void VideoPlayerApp::OnScreenSaverWillStart()
  YI_LOGI(LOG_TAG, "OnScreenSaverWillStart.");

Assigning Static Node IDs

When a Roku client is reconnecting to a server with the Disconnect On Inactivity feature, to reduce the chance of a re-export of the client’s current scene, the client node IDs generated on the server must be assigned deterministically. For this reason, Roku client node IDs are constructed using a relative path, as a sequence of child indices, of the target node starting from its root scene.

Using the relative path of scene nodes as the client node ID may fail, however, if sibling nodes are not ordered deterministically throughout the scene tree. For example, for a list of buttons that are generated dynamically based on video content information, the static ID provided might be a content-specific string such as the video media ID. As another example, optional nodes within a staged scene could be assigned a static ID to identify these nodes uniquely within the top-level scene.

Once the client reconnects to a server, the server scene tree synchronization monitor performs an export if it determines that the scene tree does not match the client. This match is based on a hash of scene tree client node IDs.

To assign static node IDs:

  1. Add the entry "useStaticNodeID" : true to serverConfig.json. To learn more about serverConfig.json, see Customizing the Cloud Solution Application Configuration File.
  2. Specify a static ID using the CloudInterface SetExportHint API with HINT::STATIC_ID.

    CYICloud::GetInterface().SetExportHint(pSceneView, HINT::STATIC_ID, "Option1");

Enabling and Disabling Disconnect On Inactivity at Runtime

There are times when you may want your application to temporarily disable the Disconnect on Inactivity system at runtime - for example, when performing an in-app purchase (IAP). A Cloud module API allows you to turn the feature on and off programmatically. Call SetDisconnectOnInactivityEnabled(false) to disable the system; call SetDisconnectOnInactivityEnabled(false) to re-enable it once the operation is complete.



const auto *pProduct = dynamic_cast<const RokuProduct *>(m_products[id].get());
CYICloud::GetInterface().RequestPurchase(pProduct->code, [this](const CloudIAPRequestStatus &status, const CloudIAPReceipt &receipt) {
  this->OnRequestPurchaseResponse(status, receipt);