Skip to main content
HandshakeTask runs RunHandshake on a worker thread. It finds a reachable server, POSTs device identity information to PATH_HANDSHAKE, and stores the result in global state. The handshake runs once at startup before authentication.

XML component definition

HandshakeTask.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="HandshakeTask" extends="Task">
  <interface>
    <!-- inputs -->
    <field id="forcedServerUrl" type="string"     value="" />
    <!-- outputs -->
    <field id="result"          type="assocarray" />
    <field id="done"            type="boolean"    value="false" />
  </interface>
  <task functionName="RunHandshake" />
</component>

Input fields

forcedServerUrl
String
Optional. When non-empty, bypasses the automatic server discovery and uses this URL as the only candidate. Useful for manual server configuration in the settings screen. The value is normalised by GTV_ServerNormalizeBaseUrl() before use.

Output fields

result
AssocArray
Populated when done becomes true. Contains the fields listed in the Result object section.
done
Boolean
Set to true when the task finishes, whether successful or not.

Result object

KeyTypeDescription
successBooleantrue when the server accepted the handshake
serverStringBase URL of the server that responded
realtimeModeStringDelivery mode returned by the server; defaults to "polling"
errorCodeIntegerHTTP code of the last failed attempt, or -1 for a network error
errorMsgStringLocalised error string on failure

Global state written

On success, the task writes the following m.global fields:
FieldSource
m.global.deviceIdroDeviceInfo.GetChannelClientId()
m.global.deviceModelroDeviceInfo.GetModelDisplayName()
m.global.osVersionGTV_NormalizeOsVersion(di) — handles string, array, and assoc-array formats
m.global.activeServerThe resolved server URL
m.global.ridaroDeviceInfo.GetRIDA()
m.global.latroDeviceInfo.IsRIDADisabled()
m.global.deviceTimezoneroDateTime.GetTimeZoneOffset()
m.global.deviceIpdeviceIp field from the server response (if present)
m.global.realtimeModerealtimeMode field from the server response

Function flow

1

Resolve server

Calls GTV_FindActiveServer(forcedServer). If forcedServerUrl is set, only that URL is tried. Otherwise, the full server probe list is used. A failure here ends the task immediately.
2

Collect device info

Reads GetChannelClientId(), GetModelDisplayName(), GTV_NormalizeOsVersion(), and GTV_GetAppVersion(). Also reads GetRIDA() and IsRIDADisabled() for ad-targeting context.
3

POST handshake

Builds a JSON payload with platform, device_id, device_model, os_version, and app_version, then calls GTV_HttpPost(url, jsonStr, TIMEOUT_HTTP). The URL is server + PATH_HANDSHAKE.
4

Retry with failover

Retries up to RETRY_MAX (3) times. On network error, advances to the next server in GTV_ServerBuildRequestSequence(). Stops immediately on HTTP 401 or 403 (auth required).
5

Parse response

Cleans and parses the JSON body with GTV_CleanJson() and ParseJson(). Reads realtimeMode and deviceIp from the response.
6

Write global state and signal completion

Writes device identity and server info to m.global, then sets m.top.result and m.top.done = true.

AppConstants values used

ConstantValueUsed for
TIMEOUT_HTTP12000 msHTTP timeout for the handshake POST
RETRY_MAX3Maximum handshake attempts across servers
PATH_HANDSHAKE"/api/v1/app/devices/handshake"Endpoint path for the handshake POST
PLATFORM"roku"Value sent in the platform field of the payload

Handshake request payload

payload = {
    platform     : c.PLATFORM,       ' "roku"
    device_id    : deviceId,          ' roDeviceInfo.GetChannelClientId()
    device_model : GTV_GetBrandedDeviceModel(di),
    os_version   : osVersion,         ' normalised OS version string
    app_version  : appVersion         ' from manifest
}

Usage example

' Run at channel startup before showing the login screen
m.handshakeTask = CreateObject("roSGNode", "HandshakeTask")
m.handshakeTask.observeFieldScoped("done", "OnHandshakeDone")
m.handshakeTask.control = "RUN"

sub OnHandshakeDone()
    r = m.handshakeTask.result
    if r.success
        ' proceed to auth check
        CheckSavedCredentials()
    else
        ShowError(r.errorMsg)
    end if
end sub

' Force a specific server (e.g. from settings screen)
m.handshakeTask = CreateObject("roSGNode", "HandshakeTask")
m.handshakeTask.forcedServerUrl = "http://192.168.1.50:3333"
m.handshakeTask.observeFieldScoped("done", "OnHandshakeDone")
m.handshakeTask.control = "RUN"
HTTP 401 and 403 responses terminate the retry loop immediately and are recorded in result.errorCode. If you see these codes, the backend requires authentication for the handshake endpoint — which is non-standard; verify your server configuration.

Build docs developers (and LLMs) love