Skip to content

Custom HTML using the stock, out-of-the-box firmware #24

@vitorio

Description

@vitorio

It seemed like extra hassle to have to update the firmware just to run a custom webapp, so here's the minimum amount of JavaScript to initialize the stock, OOTB Superbird so you can see your own HTML on it.

Unlike the Chromium in later firmware, the stock Superbird is running QtWebEngine 5.12.7, equivalent to Chromium 69.0.3497.113 (although its User-Agent reports 69.0.3497.128). The webapp is built into the qt-superbird-app, embedded as Qt resources. It can be extracted from the binary using Ghidra and the scripts described in @gipi's article, Reversing C++, Qt based applications using Ghidra, available in their repo at https://github.com/gipi/ghidra_scripts.

There's no "Control WebSocket" in the stock app when it first launches, it instead initially uses a QWebChannel, and there appear to be a few initialization commands sent back from the webapp to qt-superbird-app before it will start rendering HTML and provide messages over the QWebChannel. Not all the initialization commands are necessary to start rendering HTML, and the status messages that come in regularly over the QWebChannel can be ignored.

QtWebEngine heavily caches all the files it accesses, and manually emptying the cache(s) between runs will spare a lot of headaches when testing.

Assuming you've followed in the instructions in @frederic's https://github.com/frederic/superbird-bulkcmd repo to enable ADB (or equivalent), you can do something like this to stop the existing processes, upload an example.html, clear the caches, and launch qt-superbird-app manually using the same configuration as specified for supervisord plus a Chrome remote debugging port:

$ adb shell supervisorctl stop swupdate
swupdate: stopped
$ adb shell supervisorctl stop superbird
superbird: stopped
$ adb shell mkdir /tmp/webapp
$ adb push example.html /tmp/webapp/
example.html: 1 file pushed, 0 skipped. 0.8 MB/s (1188 bytes in 0.001s)
$ adb forward tcp:9222 tcp:9222
9222
$ adb shell rm -rf /var/cache/QtWebEngineCache/*
$ adb shell rm -rf /var/cache/qt-superbird-app/*
$ adb shell 'QT_LOGGING_RULES="" QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox --ignore-gpu-blacklist --touch-events=enabled" QT_QPA_EGLFS_INTEGRATION="eglfs_mali" QMLSCENE_DEVICE="" QT_QUICK_BACKEND="" QT_QPA_EGLFS_PHYSICAL_WIDTH="51" QT_QPA_EGLFS_PHYSICAL_HEIGHT="86" QT_QPA_EGLFS_ROTATION="-90" QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS="/dev/input/event3:rotate=270" QT_QPA_EGLFS_NO_LIBINPUT="1" HOME="/home/superbird" XDG_CACHE_HOME="/var/cache" XDG_RUNTIME_DIR="/var/cache/qt-superbird-app" QTWEBENGINE_REMOTE_DEBUGGING=9222 qt-superbird-app --config=/etc/qt-superbird-app/superbird_target.ini --url file:///tmp/webapp/example.html'
2015-01-01T03:44:04.870Z [D] main.cpp:66 BOOTMARK: main(): 0ms
2015-01-01T03:44:04.872Z [D] main.cpp:73 BOOTMARK: Config Parsed: 3ms
...

You can also run your webapp on your local machine and reverse the port to the Superbird using ADB, replacing --url file:///tmp/webapp/example.html above with --url http://localhost:8000/example.html:

$ adb reverse tcp:8000 tcp:8000
$ python3 -m http.server
Serving HTTP on port 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
...

Here's the contents of example.html, the meta elements are from the stock webapp, qwebchannel.js is built-in, the handleResponse function overloads the stock one to not delete the callbacks once they're executed to avoid some JS warnings, and the signalEmitted function is where you'd go to see the status messages coming in over the channel:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=800,height=480,initial-scale=1,maximum-scale=1,user-scalable=no">
<meta name="HandheldFriendly" content="true"/>
<title>Superbird</title>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script>
var _superbirdchannel = new QWebChannel(qt.webChannelTransport, function(channel){});
_superbirdchannel.handleResponse = function(message) {
  if (!message.hasOwnProperty("id")) {
    console.error("Invalid response message received: ", JSON.stringify(message));
    return;
  };
  this.execCallbacks[message.id](message.data);
};
_superbirdchannel.execCallbacks[0] = function(data){};
_superbirdchannel.execCallbacks[1] = function(data){};
_superbirdchannel.objects = {};
_superbirdchannel.objects.superbird = {};
_superbirdchannel.objects.superbird.signalEmitted = function(signal, args){};
_superbirdchannel.send({type: 3, id: 0});
_superbirdchannel.send({type: 7, object: 'superbird', signal: 5});
_superbirdchannel.send({type: 6, object: 'superbird', method: 9, args: [], id: 1});
</script>
</head>
<body style="background-color:#0f0;">
<h1>superbird OOTB</h1>
</body>
</html>

When run, it looks like this:

superbirdootb

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions