PlayJam GameStick server API documentation

This is an attempt to document the network API used by the PlayJam GameStick gaming console.

Common data formats

JSON responses share some properties.

date
Dates have format dd/MM/yyyy - e.g. 23/12/2013.
time
Time since 01.01.1970 in milliseconds (unix timestamp * 1000)

ping connection check

The connection check in com.playjam.gamestick.WifiTools.ChecksFragment#ping sends ICMP ping requests to three domains to see if the network is available:

GET http://connectcheck.gamestickservices.net/generate_204

Network availability ping via HTTP.

Firmware 2071 resolves the IP address for the hostname and sends the HTTP request directly to the IP address without providing a Host header.

At least firmware 0.0.53 sends the host name in the request.

HTTP request

Protocol
http
Host
  1. connectcheck.gamestickservices.com
  2. connectcheck.gamestickservices.net
  3. clients3.google.com
Path
generate_204

HTTP response

Success

Status code
204 No Content

Usage

  • com.playjam.gamestick.WifiTools.ChecksFragment#http()

    Only connectcheck.gamestickservices.net

  • com.playjam.gamestick.WifiService#ping()

    Tries all three servers until it finds one that responds.

GET http://dev-db.gamestickservices.net/api/rest/developer/validate/xxx/yyy/view.json

Verify that the GameStick may switch to the developer firmware.

HTTP request

Protocol
http
Host
dev-db.gamestickservices.net
Path

/api/rest/developer/validate//yyy/view.json;jsessionid=zzz

xxx

Hardware ID

Example: ac:db:da:09:18:5c

yyy
Verification code entered by the user
zzz
Session ID

HTTP response

FIXME

GET http://l2.gamestickservices.net/api/rest/analytics/application-event/analytics/event/view.json

Send user behavior data to the server (tracking).

Known to be used in firmware versions:

HTTP request

Protocol
http
Host
l2.gamestickservices.net
Path

/api/rest/analytics/application-event/analytics/event/view.json;jsessionid=xxx?Map=yyy

xxx
Session ID from the registration check api/rest/connect/stick/stick/xxx/view.json.
yyy

Actual tracking data:

{"NAVIGATE":"Games Featured Menu","NAVIGATE":"Media All Menu","NAVIGATE":"Profile"}

Looks like JSON, but has duplicate keys.

Known keys:

  • NAVIGATE

HTTP response

Headers
Content-Type
application/json

All data in the response must be on one line.

{"body":{"success":true}}

If the response is not successful, or the response cannot be parsed into a JSON object from one line of response data, the tracking data are sent again some minutes later.

Usage

Analytics intents are sent by the console application.

Response gets parsed in com.playjam.gamestick.databaseinterfaceservice.DatabaseInterfaceService#ParseResponse()

GET http://l2.gamestickservices.net/api/rest/game/downloadedfreegame/xxx/true/view.json

When a game has been downloaded.

HTTP request

Method
GET
Protocol
http
Host
l2.gamestickservices.net
Path

/api/rest/game/downloadedfreegame/xxx/true/view.json;jsessionid=yyy

xxx
Game ID (empty in firmware 2058)
Headers
User-Agent
Dalvik/1.6.0 (Linux; U; Android 4.1.2; GameStick V1.0 Build/V1.03.04MX01_20130911)
Connection
Keep-Alive
Accept-Encoding
gzip

HTTP response

FIXME

POST http://l2.gamestickservices.net/api/rest/connect/stick/stick/xxx/view.json

Used for several things:

  1. Network connection check
  2. Fetch registration code and session ID for this gamestick.
  3. Fetch information about games and their display in the main menu

HTTP request

When the GameStick is not registered yet, then this URL is fetched every 5 seconds. Once registered, it is fetched every 2 minutes.

The initial request does not contain a session ID. The hardware ID is used to associate the stick with an existing user ID and a new session ID is generated.

Method

POST

Firmware 0.0.53 uses GET.

DatabaseService.ServiceCore can be put into GET mode in Firmware 2071, in that case, GET will be used as well.

Protocol
http
Host

l2.gamestickservices.net

Firmware 0.0.53 uses db.gamestickservices.net.

Path

/api/rest/connect/stick/stick/xxx/view.json

xxx
Hardware-ID, e.g. ac:db:da:09:18:5c
;jsessionid=yyy
In GET mode, but only after the initial request.
POST parameters
None
Cookies
JSESSIONID

Session ID, only when available (not empty)

In POST mode only

AWSELB

When available

In POST mode only.

HTTP response

When used for connection check, the response must contain one of the following strings (no whitespace after :):

"status":"CONNECTION_IN_PROGRESS"
"status":"CONNECTED"

Not registered yet - CONNECTION_IN_PROGRESS

Status code
200 OK
{
    "sid": "dummy",
    "time": "1680109254000",
    "body": {
        "status":"CONNECTION_IN_PROGRESS",
        "registrationCode": "abcdefg"
    }
}

Registration complete - CONNECTED

Status code
200 OK
body.config.apps[].genre
Not used in firmware 2071, but still available - probably for older versions.
body.config.apps[].genres

Known genres:

  • Action
  • Adventure
  • Arcade
  • Classics
  • Media
  • Platformer
  • Puzzle
  • Racing
  • Shmup
  • Shooter
body.config.apps[].images.name

Special name STICK_SCREENSHOT adds the image URLs as screenshot, otherwise as thumbnail. (firmware 2071).

Known names:

  • STICK_THUMBNAIL1 (width 350, height 88)
  • STICK_THUMBNAIL2 (width 350, height 160)
  • STICK_THUMBNAIL3 (width 350, height 236)
  • STICK_THUMBNAIL4 (width 350, height 400)
  • STICK_VIDEO1_SCREENSHOT (width 350, height 160)
  • STICK_REGISTRATION_GAME_ICON (200x200px)
  • STICK_SCREENSHOT (width 350, height 160)
body.config.global.uitranslation
Not used in firmware 2058 and 2071.
body.config.global.newfeatured.ages[].entries[].columnentries[].thumbnail

The code says this is the "tile size".

  • 6 for full-height (one game in this column) STICK_THUMBNAIL4 is used.
  • 4 for 2/3 height (two games in this column, one size 4, one size 2) STICK_THUMBNAIL3
  • 3 for 1/2 (two games in this column) STICK_THUMBNAIL2
  • 2 for 1/3 height (3 games in this column) STICK_THUMBNAIL1
{
    "sid":"dummy",
    "time":"1680109254000",

    "lastaccessed": 1385115865500,
    "x-forwarded-for": null,
    "created": 1385115865500,
    "accessCount": 0,
    "addr": "10.37.137.31",
    "remoteaddr": "135.196.28.241",

    "body": {
        "status":"CONNECTED",
        "config": {
            "apps": [
                {
                    "id": 23,
                    "minAge": 3,
                    "name": "My game title",
                    "description": "Game description FIXME format",
                    "package": "org.example.game",
                    "size": 12345,
                    "download": {
                        "version": 123,
                        "url": "http://example.org/game.apk"
                    },
                    "genre": "Racing",
                    "genres": [
                        {
                            "genre": "Racing"
                        }
                    ],
                    "popular": 1,
                    "featured": 1,
                    "isfree": true,
                    "bought": false,
                    "downloadedfree": false,
                    "multipricing": {
                        "buy": [
                            {
                                "amount": 23.42,
                                "isocurrency": "EUR"
                            }
                        ],
                        "rent": [
                        ]
                    },
                    "images": [
                        {
                            "name": "STICK_SCREENSHOT",
                            "width": 512,
                            "height": 384,
                            "urls": [
                                {
                                    "url": "http://example.org/image.jpg"
                                }
                            ]
                        }
                    ]
                }
            ],
            "global": {
                "uitranslation": {
                    "country": [
                    ],
                    "version": 0
                },
                "newfeatured": {
                    "ages": [
                        {
                            "age": 3,
                            "entries": [
                                {
                                    "columnentries": [
                                        {
                                            "gameID": 254,
                                            "thumbnail": 12,
                                            "column": 1
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            }
        }
    }
}

Usage

  • com.playjam.gamestick.WifiTools.ChecksFragment#doDatabaseConnect()

    To check if a network connection is available. Only uses l2.gamestickservices.net.

  • com.playjam.DatabaseService.apk: com.playjam.Services.Database.ConnectDownloader

GET http://l2.gamestickservices.net/api/rest/parentcontroll/change/agerating/xxx/yyy/view.json

Change the profile's minAge setting.

Only games suitable for that age are shown by the GameStick Console UI.

HTTP request

Protocol
http
Host
l2.gamestickservices.net
Path

/api/rest/parentcontroll/change/agerating/xxx/yyy/view.json;jsessionid=zzz

xxx

Age rating:

  • 3
  • 7
  • 12
  • 17
yyy
MD5-hashed user password
zzz
Session ID

HTTP response

Successful change

Status code
200 OK
{
    "body": {
        "success": true,
        "message": null,
        "action": "ChangeAgeRating"
    }
}

action and message do not seem to be needed.

Wrong password

Status code
200 OK
{
    "body": {
        "success": false
    }
}

Neither action no message seem to be needed.

GET http://l2.gamestickservices.net/api/rest/player/profile/view.json

Fetch player information.

In firmware v2071 OOBE setup will not finish when the profile is not returned properly.

HTTP request

Protocol
http
Host

l2.gamestickservices.net

Firmware 0.0.53 used db.gamestickservices.net

Path

/api/rest/player/profile/view.json;jsessionid=xxx

xxx

Session ID from the registration check api/rest/connect/stick/stick/xxx/view.json.

Empty when not registered yet.

HTTP response

Not registered yet

Status code
200 OK

FIXME

User has been registered

Property notes:

addr

IP-Address (probably of client)

Type: string

accessCount

FIXME

Type: int

Example: 1

created

Type: int

Example: 1382005635322

lastaccessed

Type: int

Example: 1382005635738

sid

Session ID (same as in request URL)

Type: string

Example: 79C9B23DBA2682FEDFD0231A9AA28312

time

Type: string

Example: "1382005637299"

body

The actual profile data

body.accountType

Type: string

Known values:

  • MANUALY_DEFINED
  • CONSUMER
body.achievementStatus

Type: object

body.achievementStatus.lastAchievementGameName
Type: string
body.achievementStatus.numberOfAchievementsUnlocked
Type: int
body.avatarLargeUrl
Type: string
body.avatarSmallUrl
Type: string
body.action

FIXME

Type: string

body.balance

Type: object

body.balance.amountOfMoneyLeft

Type: string

Examples:

  • USD 0.00
  • GBP 25.00
body.balance.transactions

Type: array

body.balance.transactions[].amount
Type: string
body.balance.transactions[].balance

Type: string

Example: GBP 25.00

body.balance.transactions[].date

Type: string

Must contain the string " - " (space dash space). Before: date, after: description

Example: 16/10/2013 - TOP UP:PREPAID_CARD

body.balance.transactions[].description
Type: string
body.balance.transactions[].source

Type: string

Known values:

  • PREPAID_CARD
body.balance.transactions[].type

Type: string

Known values:

  • CREDIT_WALLET
body.currency

Three-letter currency code

Known values: - GBP - USD

body.dateJoined
Type: string
body.dateOfBirth
Type: string
body.email
Type: string
body.founderFlag
Type: int
body.founderName
Type: string
body.gamertag
Type: string
body.location

Type: string

Two-letter uppercase country code

Known values:

  • GB
  • US
body.message

Type: string

Does not seem to be used in 2071.

body.minAge

Type: int

Example: 17

body.minAgeLabel

Type: string

Example: 17+

body.password

Password hash (probably for age change verification)

Example: 75381f9f2bd23d8b4a0dcb0ad7c364ff

body.securityLevel

Type: int

Known values:

  • 0
  • 1
body.success

Type: string

Does not seem to be used in 2071.

{
    "sid": "sessionid",
    "time": "1680109254000",
    "body": {
        "avatarLargeUrl": "http://example.org/avatar.png",
        "gamertag": "cweiske",
        "avatarSmallUrl": "http://example.org/avatar.png",
        "location": "Somewhere",
        "dateOfBirth": "23/12/1942",
        "dateJoined": "23/12/2013",
        "email": "email@example.org",
        "password": "mypassword",
        "accountType": "FIXME",
        "minAge": 17,
        "founderFlag": 1,
        "founderName": "cweiske-kickstarter",
        "minAgeLabel": "17+",
        "securityLevel": 0,
        "balance": {
            "amountOfMoneyLeft": "not much",
            "transactions": [
                {
                    "date": "23/12/2024 - buy a game",
                    "amount": "23.42"
                }
            ]
        },
        "achievementStatus": {
            "lastAchievementGameName": "Some Game",
            "numberOfAchievementsUnlocked": 23
        }
    }
}

Usage

  • com.playjam.Services.Database.ServiceCore#downloadProfile()

GET http://l2.gamestickservices.net/api/rest/user/game/xxx/achievement/list/view.json

Fetch achievements.

Known usage:

HTTP request

Protocol
http
Host
l2.gamestickservices.net
Path

/api/rest/user/game/xxx/achievement/list/view.json;jsessionid=yyy

xxx
UUID of the game
yyy
Session ID

HTTP response

The response must be on one single line!

At least "Bloo Kid" only cares about the ID. The other properties must exist (JSON parsing fails otherwise), but their values are not used.

The id seem to be registered on the server.

Status code
200 OK
{
    "body": {
        "success": true,
        "message": null,
        "action": "ChangeAgeRating"
    }
}

Known achievement IDs:

461
Bloo Kid: Triple Hit

GET http://l2.gamestickservices.net/api/rest/wallet/payment/game//init-transaction/do/view.json

When clicking "buy" in the game details screen.

Known to be used in firmware versions:

HTTP request

Protocol
http
Host
l2.gamestickservices.net
Path

/api/rest/wallet/payment/game/xxx/init-transaction/do/view.json;jsessionid=yyy

xxx

Game ID.

Empty in firmware 2051.

yyy
Session ID from the registration check api/rest/connect/stick/stick/xxx/view.json.

HTTP response

Everything must be on one single line!

FIXME

Dummy example:

{
    "body": {
        "pageUrl": "xxx",
        "url": "yyy"
    }
}

Only one of pageUrl and url is needed, depends on request (if internal request name is webview_displaywebpage or webview_displayactivewebpage -> url, otherwise pageUrl).

This here should use pageUrl.

Usage

  • com.playjam.gamestick.DatabaseInterfaceService.apk: com.playjam.gamestick.databaseinterfaceservice.DatabaseInterfaceService#ParsePurchaseItemResponse()

GET http://l2.gamestickservices.net/api/rest/wallet/payment/topupwallet/init-transaction/do/view.json

Start adding credits to the wallet in the player profile ("Add credit").

The GameStick shows the webpage URL pageUrl in a window.

FIXME: Somehow the webpage must signal that adding credits worked.

HTTP request

Protocol
http
Host
l2.gamestickservices.net
Path

/api/rest/wallet/payment/topupwallet/init-transaction/do/view.json;jsessionid=xxx

zzz
Session ID

HTTP response

Status code
200 OK
{
    "body":{
        "success": true,
        "pageUrl": "http://example.org"
    }
}

POST http://update.gamestickservices.net/check.php

Check if a new firmware update is available.

Three hosts are checked one after another. When update.gamestickservices.com is not available, it may take up to two minutes until the .net host is tried.

HTTP request

Protocol
http
Host

Three hosts are checked in firmware 2071:

  1. update.gamestickservices.com
  2. update.gamestickservices.net
  3. 54.215.8.117
Path
check.php
Headers
Content-Type
application/x-www-form-urlencoded
POST parameters
v

JSON-encoded hardware information:

{
    "hwid": "ac:db:da:09:18:5c",
    "major": 0,
    "minor": 0,
    "revision": 53,
    "platform": 0
}

HTTP response

JSON must be one a single line; parsing will fail otherwise.

Update available

Status code
200 OK
{
    "available": true,
    "major": 0,
    "minor": 0,
    "revision": 53,
    "forced": false,
    "name": "v2.23.42",
    "description": "Update now immediately! We have new features.",
    "timestamp": 1680271986000,
    "url": "http://example.org/firmware.php?version=2071"
}

No update available

Status code
200 OK
{
    "available": false
}

Usage

  • com.playjam.gamestick.WifiTools.ChecksFragment#doUpdateConnect()
  • com.playjam.UpdateService

Update download process

Firmware updates are downloaded by a separate downloading process in com.playjam.UpdateService.

Updates are downloaded in small chunks that are combined to a single file in the end. Firmware updates are standard Android OTA .zip update files that were renamed to .img by PlayJam. (GameStick-Software-v2071.img).

The download URL given in the HTTP reponse is appended with &i= plus the chunk index number.

Two special chunk numbers exist:

-2
Returns the total size of the download (plain text long value)
-1
Returns the number of chunks (plain text, int)

-2 is fetched first, -1 second and then 0, 1 and so forth.

After download and chunk combination, the file is verified by standard Android mechanism android.os.RecoverySystem.verifyPackage().

Information about the new firmware is written to /data/GameStickCache/update.info.

Chunks

Generating chunk files:

  1. The firmware update file must be split into files of 102400 bytes.
  2. Each part must be XORed with 91 (hex 5b, binary 1011011).
  3. The binary SHA1 sum of the XORed part file must pre prepended before the data

Having a single chunk file only should work, too.

GET http://www.playjam.com/bundles/system/meta.json

Fetch link to the URL of the latest Companion App configuration file.

Used by the companion app.

HTTP request

Protocol
http
Host
www.playjam.com
Path
/bundles/system/meta.json

HTTP response

Property notes:

timestamp

Type: long. Optional.

0 to disable Companion App bundle file download.

bundleURL
Path to .zip file with configuration data.

Example:

{
    "timestamp": 0,
    "bundleURL": null
}

Usage

  • com.playjam.companionapp.service.CAServerService#fetchSystemBundle()

About

This documentation has been written by Christian Weiske, cweiske+ouya@cweiske.de.

Last update: 2023-06-18T08:47:09+02:00

License

It is licensed under the GNU Free Documentation License.

Source code

The documentation sources are available at https://git.cweiske.de/playjam-gamestick-api-docs.git and mirrored at https://codeberg.org/cweiske/playjam-gamestick-api-docs

Home page

A rendered version of this documentation is available at http://cweiske.de/gamestick-api-docs.htm

Building

You need to install rst2html5 before:

$ pip install rst2html5-tools

Rendering the docs is done via a build script:

$ ./build.sh