The latest posts in full-text for feed readers.
Ich war im Podcast "Besser Wissen" von Golem.de und habe mit Martin Wolf in der Folge Die OUYA lebt! über die OUYA, deren Untergang und die Rettung geredet.
Siehe auch: Initialer Blogpost, alle OUYA-Blogposts und ouya.cweiske.de
Published on 2024-04-05 in ouya
Es ist das Jahr 2024, 26 Jahre nach der Standardisierung von IPv6. Die 1blu RootServer unterstützen kein IPv6, wie auch die meisten anderen Produkte bei 1blu. Traurig.
Der Support meint:
Wir bedauern Ihnen mitteilen zu müssen, dass IPv6 bei Ihrem Produkt aktuell noch nicht unterstützt wird. Unsere Technik arbeitet bereits daran dies zu ermöglichen. Bitte haben Sie Verständnis, dass wir Ihnen jedoch noch keinen genauen Termin nennen können.
Siehe auch:
Published on 2024-03-05 in bigsuck, network
I'm using a Fairphone 4 smart phone, running the MicroG-enabled LineageOS instead of the default software (ROM).
Unfortunately, the phone has several shortcomings:
Sometimes the touch screen triggers scrolls, swipes and zoom gestures without anyone touching it.
Thousands of FP4 users had this problem, and it was fixed in the official Fairphone software version Fairphone OS version B.086:
Ghost touches: an issue that causes the user interface to move or jump without touching the screen.
Unfortunately the fix is not available on LineageOS 20 (2024-01-05), which means I still experience the problem.
I once disassembled the phone, "cleaned" the display contacts by blowing onto it and re-assembled it. Afterwards the ghost touches appeared less often.
Enabling "touch visualization" in the Android developer settings gave me this screenshot of ghost inputs:
Sometimes the touch screen does not work (does not recognize touches) when the phone is lying flat on a table or my lap. When touching the frame it becomes responsive again.
Other people also reported this problem:
All we know is that there is some empirical evidence that the FP4’s touchscreen now requires the phone to be connected to some larger conductive body to reliably work – Either the user also touching the phone’s chassis, or the phone being connected to something else by its USB cable (like while charging it).
KurtF, 2023-08-26, Software update: FP4.SP2J.B.086.20230807
The Fairphone 3 already had this problem:
Seit ich das FP3+ habe funktioniert das Touch Display nicht,
wenn ich es auf den Tisch lege.
Wenn ich es in die Hand nehme, ist alles in Ordnung.
Lotta_Hund, 2021-05-06,
FP3+ Touch Display funktioniert nicht, wenn es liegt
I can't listen to podcasts via the Fairphone 4's built-in speakers because the sound is awful. My ring tone and message notification sounds also sound very tinny.
This hardware problem is known and acknowledged by the Fairphone developers:
Time to call it. It’s a hardware problem that we could mitigate in software by artificially reducing the volume of the top speaker. We believe that a blanket volume reduction will be perceived negatively by the majority of users, so we opted to leave it as it is.
The workaround described above (Accessibility Settings > Mono > balance all the way to the left) works in most cases. [...]
We’ve learned a lot from this investigation and we’ll use these lessons when designing future products.
Yasen_Tomov, 2023-06-08, Check for and replace blown speaker
For me the workaround is to use the Android accessibility settings to downmix all sounds to mono and move them to the right speaker (bottom of the phone), completely bypassing the right speaker (top of the phone).
I love to use SkyMap (F-Droid) to see which stars are visible in the sky at night.
Unfortuantely the compass on the Fairphone 4 is so bad that the map just jumps around and shows a totally different direction when rotating the phone for a couple of degrees.
As usual, other people have the same problem:
In almost all cases, the orientation error ranges from 45 to 180 degrees.
ChristianH, 2023-01-25, FP4: The compass is unreliable
Moving the phone in a "8"-pattern several times while Skymap or the compass app is open did improve accuracy quite a bit, but I have to recalibrate every time I use Skymap.
Published on 2024-02-25 in android, hardware
The OUYA's servers were shut down in 2019,
and Google shut down the servers for its Stadia
game console in
january 2023.
I thought it would be cool to use the controller of one dead console
on a resurrected dead gaming console :)
The Stadia controllers did connect directly to Google's internet servers via WiFi and only used Bluetooth for initial setup. After shutdown, Google provided a website that let you install a controller firmware update, finally making the controller a standard Bluetooth one.
The Stadia controller only supports Bluetooth Low Energy
(BLE).
The OUYA
contains
a AzureWave AW-NH660 chipset that is based on BCM4330,
whose datasheet says it is
forward-compatible with the impending Bluetooth Low Energy operating mode
.
Android supports BLE
starting from version 4.3:
Bluetooth Low Energy (BLE), available in Android 4.3 and later, creates short connections between devices to transfer bursts of data
The OUYA runs Android 4.1, so you cannot use BLE controllers on an OUYA.
The Stadia controller has a USB-C port. Connecting it to the OUYA's own USB-A port gives you a partially working controller: The d-pad, right thumb stick and L2+R2 do not work at all - only the left thumb stick works.
A custom key layout file is needed, just as I did before with a playstation controller.
Connect the controller via USB to your Android device and then run getevent -i in a terminal, e.g. via adb shell:
$ getevent -i
add device 2: /dev/input/event0
bus: 0003
vendor 18d1
product 9400
version 0111
name: "Google LLC Stadia Controller rev. A"
location: "usb-tegra-ehci.2-1/input1"
id: "99200YCAC2LJ5L"
version: 1.0.1
events:
KEY (0001): 0072 0073 00a4 0130 0131 0133 0134 0136
0137 013a 013b 013c 013d 013e 0140 0141
0142 0143
ABS (0003): 0000 : value 0, min 1, max 255, fuzz 0, flat 15, resolution 0
0001 : value 0, min 1, max 255, fuzz 0, flat 15, resolution 0
0002 : value 0, min 1, max 255, fuzz 0, flat 15, resolution 0
0005 : value 0, min 1, max 255, fuzz 0, flat 15, resolution 0
0009 : value 0, min 0, max 255, fuzz 0, flat 15, resolution 0
000a : value 0, min 0, max 255, fuzz 0, flat 15, resolution 0
0010 : value 0, min -1, max 1, fuzz 0, flat 0, resolution 0
0011 : value 0, min -1, max 1, fuzz 0, flat 0, resolution 0
MSC (0004): 0004
input props:
<none>
Here we see that the controller has 18 buttons (keys) and 8 axes.
Now run getevent without parameters, press the buttons and axes and make notes of their IDs:
$ getevent
[...]
/dev/input/event1: 0004 0004 00090001
/dev/input/event1: 0001 0130 00000001
/dev/input/event1: 0003 0000 0000007f
/dev/input/event1: 0003 0001 0000007f
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0004 0004 00090001
/dev/input/event1: 0001 0130 00000000
/dev/input/event1: 0003 0000 0000007c
/dev/input/event1: 0003 0001 0000007d
/dev/input/event1: 0000 0000 00000000
Here button A was pressed and released - it has the Linux key code 0x130. Doing that for all the buttons gives us this list:
A 0x130 B 0x131 X 0x133 Y 0x134 ... 0x13a === 0x13b L1/LB 0x136 R1/RB 0x137 L3 0x13d R3 0x13e Stadia 0x13c
Now do the same for the axes:
D-Pad left-right 0010 up-down 0011 Left stick left-right 0000 up-down 0001 Right stick left-right 0002 up-down 0005 Left trigger 000a Right trigger 0009
And this is all the information needed to write the key layout file:
# Google Stadia Controller via USB key 0x130 BUTTON_A key 0x131 BUTTON_B key 0x133 BUTTON_X key 0x134 BUTTON_Y #L1 key 0x136 BUTTON_L1 #R1 key 0x137 BUTTON_R1 #L3 Stick key 0x13d BUTTON_THUMBL #R3 Stick key 0x13e BUTTON_THUMBR #Stadia symbol key 0x13c HOME #=== key 0x13b MENU #... key 0x13a BACK #D-pad axis 0x10 HAT_X axis 0x11 HAT_Y #Left stick axis 0x00 X axis 0x01 Y #Right stick axis 0x02 Z axis 0x05 RZ axis 0x0a LTRIGGER axis 0x09 RTRIGGER
Copy that file into /system/usr/keylayout/:
$ adb push Vendor_18d1_Product_9400.kl /sdcard/
$ adb shell
$ su
$ mount -o remount,rw /system
$ mv /sdcard/Vendor_18d1_Product_9400.kl /system/usr/keylayout/
Simply unplugging and replugging makes Android load the custom key layout file instead of Generic.kl, and all buttons work.
8BitDo sells the USB Wireless Adapter 2, which bridges Bluetooth controllers to USB ports with the added benefit of letting you customize the button mappings. It supports Bluetooth Low Energy and - since firmware v1.04 - the Stadia controller:
Added support for Google Stadia Controller. (The controller is required to update to the Bluetooth mode firmware, it does not support vibration)
The adapter can behave differently depending on its mode, which can be switched when holding ··· and the directional pad keys for 3 seconds. You will see the adapter's red LED flash.
Supported modes as of firmware 1.06:
I opted to use Dinput mode (··· + d-pad left), and could use the Stadia USB key layout file without changes - I only had to give it a new name to match the USB IDs: Vendor_2dc8_Product_3105.kl!
On Android, multiple stages of input mapping take place:
On Linux and Android you can see the keys a controller supports by inspecting its device information:
$ cat /proc/bus/input/devices
I: Bus=0003 Vendor=2dc8 Product=3105 Version=0111
N: Name="8BitDo 8BitDo Receiver"
P: Phys=usb-tegra-ehci.2-1/input0
S: Sysfs=/devices/platform/tegra-ehci.2/usb2/2-1/2-1:1.0/input/input8
U: Uniq=E417D86076B0
H: Handlers=js0 event1
B: PROP=0
B: EV=1b
B: KEY=7fff0000 0 0 0 0 0 0 0 0 0
$ cat /sys/devices/platform/tegra-ehci.2/usb2/2-1/2-1\:1.0/input/input8/capabilities/key
7fff0000 0 0 0 0 0 0 0 0 0
This key bit mask value 7fff0000 in bits is:
1111111111111110000000000000000
which are 15 buttons/keys.
The input driver selected by Linux then takes care of mapping the bits onto key codes. You can inspect this information in the /sys/ file system as well:
$ cat /sys/devices/platform/tegra-ehci.2/usb2/2-1/2-1\:1.0/input/input8/modalias
input:b0003v2DC8p3105e0111-e0,1,3,4,k130,131,132,133,134,135,136,137,138,139,13A,13B,13C,13D,13E,ra0,1,2,5,9,A,10,11,m4,lsfw
With the help of the Linux kernel sources input.c and input.h this can be decoded:
b0003 #bustype v2DC8 #vendor p3105 #product e0111- #version e0,1,3,4, #evbit k130,131,132,133,134,135,136,137,138,139,13A,13B,13C,13D,13E, #keybit r a0,1,2,5,9,A,10,11, #relbit m4, #mscbit l #ledbit s #sndbit f #ffbit w #swbit
The key codes that were used for the key map file above are already visible here.
During development of my key layout file I got the following message from the OUYA framework's input handler:
E/OuyaInputMapper( 548): onInputDeviceAdded device=5 not found D/ControllerInputDeviceListener( 565): InputDevice 8BitDo 8BitDo Receiver (id: 5, desc: 9c597667366a6b3d65e77ad35e1bc1f757d19732) is not a gamepad...
It turned out that I had used Android key code values (2xx range) instead of Linux key codes (3xx/0x13x range) in the key layout file as keys. After correcting the codes to 0x130 and above, the controller was correctly detected as gamepad.
When the Stadia controller is connected via USB to the OUYA and the Stadia button glows orange instead of white, then you have to reboot the OUYA.
This often happens when connecting the USB cable 5+ minutes after bootup.
Published on 2024-02-05 in ouya
I tried to get a Windows 8.1 system running inside VirtualBox 7.0.12,
but it fails as soon as I install the Guest Additions
.
All Windows updates have been installed. When installing the Guest Additions, there are 4 errors about Windows preventing the installation of unsigned drivers.
After rebooting, Windows sees something is broken and will do an automatic repair, which yields no results. Rebooting does not help, the system is broken beyond repair :(
Bug report: #21979: Installing Guest Additions in Windows 8.1 leads to non-bootable system
Published on 2024-02-05 in bigsuck
Nach dem Erwärmen von Milch in unserer 20 Jahre alten Mikrowelle vom Typ Severin MW 7840 drückte ich den Türöffnerknopf und nichts passierte: Die Tür blieb einfach zu; wiederholtes Drücken brachte auch nichts.
Also die Mikrowelle aufgeschraubt und da war das Problem: Das Drücken des Türöffners ist eine horizontale Bewegung, und im Inneren gibt es eine Umlenkeinrichtung, die diese in eine vertikale Bewegung nach oben umwandelt. Oben ist dann ein Schalter, der die Tür dann öffnet (und die Mikrowelle sicherheitshalber ausschaltet).
Der Umlenker ist mit 2 Nippeln beweglich im Gehäuse befestigt, und einer davon war abgebrochen:
Ein Freund besitzt einen 3D-Drucker. Er fand ein ähnlich aussehendes Modell eines Umlenkers und druckte es aus - leider passte es nicht.
Dann griff er selbst zu Lineal und Winkelmesser und baute die Umlenkeinrichtung am Computer nach. Als Extra gab es noch eine Stabilisierung des Nippels, damit das Ersatzteil länger hält als das Original:
Das
3D-Modell des Ersatzteils für die
Mikrowelle mit Grill "electronic plus" MW 7840
ist auf printables.com herunterladbar.
Published on 2024-01-28 in hardware
Sometimes I download images from The Internet™ for later use. For reference I'd like to store some meta data inside the image itself:
The question is now: Which meta data field should I use for those URLs?
Basic meta data can be stored in the image's EXIF data, but there is no URL field:
$ exiftool -list -EXIF:All|grep -i url
$
The Exif 2.3 metadata for XMP document also does not list a single URL field, so exiftool was right.
The XMP standard is another way to store meta data in files. XMP Specification Part 1 defines multiple fields that one could use:
Property | Type | Description |
---|---|---|
dc:relation | Unordered array of Text | A related resource. |
dc:source | Text | A related resource from which the described resource is derived. |
Unfortunately there are no "real" URL fields.
There is another vocabulary, the IPTC Photo Metadata Standard.
Property | Type | Description |
---|---|---|
Iptc4xmpCore:Source | Text | The name of a person or party who has a role in the content supply chain. |
Iptc4xmpCore:CreatorContactInfoCiUrlWork | URL, multiple |
The creator's contact information provides all necessary information to get in contact with the creator of this item and comprises a set of sub-properties for proper addressing. The contact information web address part. Multiple addresses can be given, separated by a comma. |
plus:ImageSupplier | Seq ImageSupplierDetail | Identifies the most recent supplier of the item, who is not necessarily its owner or creator. For identifying the supplier either a well known and/or registered company name or a URL of the company's web site may be used. |
Only Iptc4xmpCore:CreatorContactInfoCiUrlWork sounds like it could be used to identify the web site that linked to the image, but I think it is meant to directly link to the creator's homepage - and not to a random URL that just contains a image tag.
The Metadata Working Group published the Guidelines for Handling Image Metadata spec in 2010, and it contains a tag that actually matches my idea of "URL of website that linked to the image":
Property | Type | Description |
---|---|---|
mwg-coll:CollectionURI | URI | URI describing the collection resource. |
A "collection" in MWG speak is a group of images that this specific image is part of. And a website that links to the image can be seen as such a group.
I'm not satisfied with the available properties I found. But instead of inventing my own namespace with source and website properties, I'll simply use the Dublin Core XMP properties:
Property | Usage |
---|---|
|
URL of image that was downloaded |
dc:relation | URL of website that linked to the image |
Let's say that I visited http://cweiske.de/bdrem.htm and downloaded the image http://cweiske.de/graphics/bdrem/html.png. Now I want to add the website URL and image URL to its meta data.
Embedding the URLs in the downloaded image is easy with exiftool:
$ wget http://cweiske.de/graphics/bdrem/html.png -O bdrem-html.png
$ exiftool -source=http://cweiske.de/graphics/bdrem/html.png\
-relation=http://cweiske.de/bdrem.htm bdrem-html.png
Warning: [minor] IPTC:Source exceeds length limit (truncated)
1 image files updated
Despite the warning, the full source URL is stored in the image file. But on JPG files the source is really truncated:
$ exiftool -S -source -relation bdrem-html.jpg
Source: http://cweiske.de/graphics/bdrem
Relation: http://cweiske.de/bdrem.htm
To work around this issue, we force exiftool to use the XMP source property instead of the IPTC source property:
$ exiftool -XMP:source=http://cweiske.de/graphics/bdrem/html.png\
-XMP:relation=http://cweiske.de/bdrem.htm bdrem-html.jpg
1 image files updated
Extracting the data is also possible:
$ exiftool bdrem-html.png
ExifTool Version Number : 9.46
File Name : bdrem-html.png
...
MIME Type : image/png
Image Width : 463
Image Height : 122
...
Software : Shutter
Source : http://cweiske.de/graphics/bdrem/html.png
XMP Toolkit : Image::ExifTool 9.46
Relation : http://cweiske.de/bdrem.htm
Image Size : 463x122
$ exiftool -S -source -relation bdrem-html.png
Source: http://cweiske.de/graphics/bdrem/html.png
Relation: http://cweiske.de/bdrem.htm
Adding the meta data manually is possible, but it would be best if they were
added automatically when saving-by-right-clicking the image in my brower.
Unfortunately, no browser supports this.
On MacOS, downloaded files have the download source in the "Where from" file information. Safari, Chrome and Firefox (bug, commit) support this.
It is stored as extended attribute com.apple.metadata:kMDItemWhereFroms in the file system, so it is not tied to the file itself (but also does not modify the file, and works for all types of files).
2024-01: Kelvin Thompson sent me an e-mail explaining that exiftool allows to access this attribute and copy it into a different tag in the file itself:
$ exiftool '-XMP:source<MDItemWhereFroms' filename.jpg
The XDG defines a list of Common Extended Attributes, among them is user.xdg.origin.url. It shall be used as extended file system attribute, similar to what MacOS does.
curl supports writing this file system attribute:
$ curl --xattr --output html.png http://cweiske.de/graphics/bdrem/html.png
$ getfattr --dump html.png
# file: html.png
user.mime_type="image/png"
user.xdg.origin.url="http://cweiske.de/graphics/bdrem/html.png"
Chromium once supported it, but
removed it in 2019
because
metadata doesn't provide any security guarantees on Linux, and is a privacy risk
.
wget, just like curl, supports the --xattr option:
$ wget --xattr http://cweiske.de/graphics/bdrem/html.png
$ getfattr --dump html.png
# file: html.png
user.xdg.origin.url="http://cweiske.de/graphics/bdrem/html.png"
Firefox has a feature request open since 2011 for supporting user.xdg.origin.url.
But it does write the origin URL to gnome gvfs meta data:
$ gio info --attributes=metadata:: html.png
[...]
attributes:
metadata::download-uri: http://cweiske.de/graphics/bdrem/html.png
Daniel Aleksandersen suggested that we already have a property that means "URL of the thing we talk about", and that is rdf:about:
[...] goes on to identify the resource the statement is about (the subject of the statement) using the rdf:about attribute to specify the URIref of the subject resource.
Published on 2016-07-05 in photos, tools
Sicherungskopien von DVD-Filmen liegen bei uns auf einem NAS, welches per NFS auf der Dreambox DM7080HD verfügbar gemacht wird. Aus Sicherheitsgründen ist der NFS-Ordner im NAS für die Dreambox als "nur lesen" markiert, damit ein aus versehenes "Löschen" auf der Dreambox nichts kaputt machen kann.
Leider führt das dazu, daß der Abspielfortschritt bei Filmen nicht gespeichert wird, denn das erfolgt bei der Dreambox über .meta-Dateien, die im gleichen Verzeichnis gespeichert werden wie die Filmdatei.
Eine Lösungsmöglichkeit wäre die Überlagerung des Filmverzeichnisses vom NAS mit einem schreibbaren, so daß die Abspielstati zwar scheinbar im gleichen Verzeichnis, intern jedoch woanders gespeichert werden. Der Linux-Kernel der DM7080HD hat Version 3.4, overlayfs ist allerdings erst ab 3.18 im Kernel enthalten.
Seitdem ich mein altes Synology-NAS durch eine Kombination aus Homeserver (Bereitstellung der NFS-Freigaben) und Backupserver (tägliche Sicherungen vom Homeserver) ersetzt habe, ist eine andere Lösung möglich: overlayfs direkt auf dem Server, und die Dreambox bindet die Freigabe im Lese+Schreibmodus ein.
+----------+ +--------------------------------+ | Dreambox | --[Netzwerk]-> | NFS-Freigabe "dreambox-videos" | +----------+ | | | | [überlagert] | | v | | normaler Filmordner | +--------------------------------+
Damit kann die Dreambox ihre Metadateien in die Freigabe "dreambox-videos" des Homeservers schreiben, während die Filme darunter vor Veränderungen geschützt sind.
Ordnerstruktur auf dem Server:
/data ├ videos # Originaler Filmordner └ dreambox ├ overlay-working # temporäres Verzeichnis für das Overlay ├ videos # NFS-Freigabe für die Dreambox └ videos-changes # Dateien, die die Dreambox schreibt
Das Overlay wird automatisch beim Start des Server bereitgestellt:
overlay /data/dreambox/media x-systemd.requires-mounts-for=/data,nfs_export=on,lowerdir=/data/videos,upperdir=/data/dreambox/videos-changes,workdir=/data/dreambox/overlay-working
Wichtig ist hier das nfs_export=on, weil es sonst zu folgendem Fehler kommt:
$ exportfs -r exportfs: /data/dreambox/media does not support NFS export
Der NFS-Export ist folgendermaßen konfiguriert:
/data/dreambox/videos dreambox(rw,sync,no_wdelay,no_subtree_check,no_root_squash,insecure_locks,anonuid=0,anongid=0)
Published on 2023-12-27 in dreambox
I'm on the quest to make PlayJam GameStick micro consoles usable again. Building a replacement server is underway, and collecting game information is nearly finished - but not much progress has been made obtaining the actual .apk files.
37 apks could be downloaded from the PlayJam Amazon AWS S3 bucket, but the rest of them are not accessible although we know the URLs. They are probably only accessible with a temporary access token that the GameStick server once generated.
We contacted a number of developers and asked for the apks, but most don't have them anymore - they are lost. Some developers don't even remember having programmed for the GameStick :) And many don't react on e-mails.
The last resort is to get the .apk files off the GameSticks that have them installed. PlayJam wanted to prevent that, so they locked the GameStick down, preventing access to the Android settings, disabling the adb daemon and even removing the functionality to sideload games (which was available in early firmwares).
Simply starting the Android settings or adbd via the advanced launcher TOFU plugin did not work. I needed to find a way to inspect the GameStick and run applications on them to see what worked. Because adb access was not available, I decided to build my own telnet server that would run as TOFU plugin on the GameStick.
I found a nice Python 2 socket server example and used that as base to build my own server that I could connect to with a telnet client. Development was easy because I could run the server locally. Only at the end I built a TOFU plugin that simply started the telnet server script.
One thing I learned was that the shell (my server) needs to take care of storing the current working directory, and also needs to implement change directory commands - those cannot be delegated to the system. After 2.5 hours I had a telnet server that ran on the GameStick, and I could finally inspect the file system and run programs:
cweiske:~/dev/gamestick/sync/tofu-plugin> telnet gamestick 5023
Trying 192.168.3.41...
Connected to gamestick.
Escape character is '^]'.
/> ls
acct
backup
boot
cache
[...]
/> whoami
u0_a40
/> am start -n com.android.settings/.Settings
[Errno 13] Permission denied
Now I could try to open the Android settings (which still fails) and start adbd - it turned out that running programs on the GameStick is only possible as root:
$ telnet gamestick 5023
Trying 192.168.3.41...
Connected to gamestick.
Escape character is '^]'.
/> su -c start adbd
/> quit
Connection closed by foreign host.
cweiske:~/dev/gamestick/sync/tofu-plugin> adb connect gamestick
already connected to gamestick:5555
cweiske:~/dev/gamestick/sync/tofu-plugin> adb shell
root@android:/ # pwd
/
root@android:/ # ls /data
GameStick
GameStickCache
The result of this endeavour are two TOFU plugins:
Update 2023-06: An adbd-enabling firmware image is available at codeberg.org/gamestick-fans/firmware-adb-enabler/ - see GameStick: Black screen after OOBE.
Published on 2023-05-07 in gamestick
Work on my own server for the abandoned PlayJam GameStick micro console continued, and two weeks ago I deemed it good enough to make it public for others to test.
To make sure everything works fine I removed the GameStick host name entries from my local DNS server, factory reset the GameStick and started the initial setup. It all worked fine as expected, but after completing setup the screen went black and stayed that way. Only notifications that a Gamepad was connected/disconnected or the internet connection was established were shown in the bottom right corner. The GameStick did not react to gamepad nor keyboard input.
Since had reinstalled the 2071 firmware as part of my test, I had no access to adb - for that I had to install and start the TOFU media player, install and run my "start adb" plugin. None of this was an option since the main UI ("Console") did not load.
Flashing firmware 2058 gave the same black screen that 2071 showed.
I suspected something to be off with my API and changed the connect API responses to known-to-work versions from git history of the playjam gamestick API code, but that did not help. Minimizing them did not help either.
Next I tried to replace the profile API with a version that I used when I got it working for the first time, but that did also not help.
I also suspected the session IDs to have invalid characters, but changing them did not yield any results. The black screen stayed.
Another thing that the official connect API responses had were UI translation strings, so I integrated them - but no avail.
The first three days of poking in the dark were over.
During development I had collected a number of server responses that were cached on different GameSticks (Toast, Cataphoresis, Ryo, Kazdan, Lee Chapman). I let my server return those files, but the screen still stayed black.
Since I knew that the UI did not show the latest data when they were downloaded from the server, I had to boot the GameStick once to the black screen, wait for it to download all API data, and rebooted again to see if the new data made any difference.
Nothing.
I also replaced the small .jpg profile images with original large .png files.
From previous experiments I knew that OOBE in firmware 2071 did not show the profile image, while 2058 does. So I tried to find out what changed between those two firmware versions: 2058 used the custom JSON handler, while 2071 had completely switched to a Gson to parse and hydrate the API responses.
There were no differences to find in the parsed properties. Day 4 over.
My main problem was that I did not have error logs because the PlayJam developers had disabled adbd on the GameStick. My next idea was to install all the GameStick .apk files inside the Android emulator and see the error in its logs.
Fortunately I could configure a 4.1 Android system for the emulator, start it and install all com.playjam.* apk files that were part of the 2071 firmware. I could start OOBE, but it would hang in the 5th step, the activation. Logs said that it could not obtain the hardware ID which is needed in requests to the API server.
I dived deep into the decompiled code, found out how the GameStick loads its hardware ID, built an Android app that provides a system service that starts on boot and sends out the com.playjam.SYSTEM_INFO intent with a fake hardware ID and firmware version information.
I learned the hard way that building an app with only a service and without any user interface will never be marked as "activated", and thus its services will never be started by the Android system. So I had to build a dummy UI activity that started the service once.
In the end, my service worked and the GameStick apks could fetch the correct API from the server, and OOBE finished.
The Console UI had some errors because certain files and folders were missing in the /data user partition. I copied them over from the firmware image, and the GameStick intro video played!
Unfortunately the UI crashed while the spinner was rotating, because playing a sound did not work in the emulator:
W/AudioPolicyManagerBase( 654): getOutput() could not find output for stream 3, samplingRate 0,format 0, channels 3, flags 0 E/AudioTrack-Java( 2246): [ android.media.AudioTrack ] getMinBufferSize(): error querying hardware W/dalvikvm( 2246): JNI WARNING: JNI method called with exception pending W/dalvikvm( 2246): in Lcom/ideaworks3d/marmalade/LoaderThread;.runOnOSTickNative:()V (GetObjectClass) W/dalvikvm( 2246): Pending exception is: I/dalvikvm( 2246): java.lang.NullPointerException: I/dalvikvm( 2246): at com.ideaworks3d.marmalade.SoundPlayer.start(SoundPlayer.java:75) I/dalvikvm( 2246): at com.ideaworks3d.marmalade.LoaderThread.soundStart(LoaderThread.java:856)
But this was a kind of success: The intro video was not visible on my real factory-reset GameStick.
Day 6 over.
So my GameStick stays completely black but the emulator shows the intro video: I needed to get back to real hardware. One thing I had not yet tried was CFW 1.4, the Custom FirmWare by shanti (GameStickers.net: [ROM] CFW v1.3 - Updated 1.4 is out :D, archived version). It hopefully has adbd enabled, which would give me logs.
I flashed it onto the GameStick, and it booted into a standard Android user interface (the whole point of the firmware was to get an usable Android without any restrictions). Then I installed all the com.playjam.*.apk files and populated /data as I had when using the emulator.
The results were good: OOBE worked, intro video plays, the spinner is visible and then the normal UI starts and is usable - only game images did not load.
But while it was nice that I got the official UI working on custom firmware, I did not find out why the official firmware stayed black.
Day 7 was gone.
When inspecting the CFW 1.4 files I noticed /system/build.prop which contained three additional lines:
persist.service.adb.enable=1 persist.service.debuggable=1 persist.sys.usb.config=mass_storage,adb
If the official firmware had this, I'd get proper error logs and could see the error messages.
I learned that the GameStick firmware .img files are standard Android OTA .zip files and not some custom format.
I also learned that firmware files need to be signed, so I used the standard java method for signing:
$ signapk -a 4 --min-sdk-version 16 --disable-v2 certs/certificate.pem certs/key.pk8 image.tmp.zip image.signed.img
.. but when trying to flash that image I got:
Verifying update package...
Installation aborted.
I tried different parameter combinations (align/noalign, v1/v2) but all failed.
Then I read all 28 pages of the archived CFW thread on gamestickers.net and found someone who asked how the image was signed. shanti had answered:
here is the program I used: "Sign-em! 2.0"
http://forum.xda-developers.com/showthread.php?t=1966007
I got the linux version and found it used signapk.jar. After finding that I saw that it used some internal old Sun Java class sun.misc.BASE64Encoder that was not available anymore in any recent OpenJDK :(
Android system recovery normally only installs firmware images
that have been signed by one of a number of white-listed keys.
The GameStick's recovery seems to allow all properly signed images,
regardless which signing key was used.
This is the reason the CFW was possible at all
- on OUYA, custom firmwares could only be installed when using a
custom bootloader,
but not with the stock recovery that requires firmwares to be signed
by a key that only OUYA possessed.
Then I found the next XDA thread post that linked to HemanthJabalpuri/signapk which contained MinSignApk 1.0 that ran on current Java versions!
I signed my firmware image with MinSignApk, and could flash it!
Update 2023-06: An adbd-enabling firmware image is available at codeberg.org/gamestick-fans/firmware-adb-enabler/
Now that I had flashed my custom firmware 2071+adb I finally saw the errors:
I/ActivityManager( 3509): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.playjam.gamestick.console cmp=com.playjam.gamestick.console/.Main u=0} from pid 3754 D/GameStick( 4262): **************************************** D/GameStick( 4262): Failed to create download directory
The directory existed, so I removed it:
$ adb shell rmdir /data/data/com.playjam.consoledev/files/Downloads
Next boot, the GameStick would create the directory, and the following it would complain again. So this was not the problem.
I/ActivityManager( 4479): Displayed com.playjam.gamestick.console/.Main: +313ms (total +1s416ms) W/System.err( 4628): java.io.EOFException W/System.err( 4628): at libcore.io.Streams.readAsciiLine(Streams.java:203)
The firmware update check needs to return its JSON on a single line, otherwise parsing would fail. I modified the server.
D/PlayjamKeyboard( 3685): Starting for type : 0/0 I/marmalade( 4262): PJException error has occurred : 62 c:/_work/dev/marmalade_main/menu_head/src/PNGFile.cpp OpenTextureFile open png Error : empty file name
This finally looked like the real problem. I removed all game information from the API in case the game images were the culprit, as well as the profile images. Still the same error.
Day 8 was over.
The next day I tried more image related changes:
While booting the GameStick I came across a very special error:
W/DatabaseService:ConnectDownloader( 3794): Failed to duplicate connect data for console : /data/GameStick/ConsoleResources/315f7b66-fae7-4ab3-9d46-bf41942694b6.json => /data/GameStickCache/reg_server_response.json
Reading through the source code led to me understanding that process:
The last step failed because downloading fresh data (500 kiB!) was faster than parsing cached data.
This could only happen on my local setup: GameStick's WiFi reception is so bad that I could not use my normal access points. Instead I enable an access point on my laptop whenever I do GameStick development, and the GameStick only needs to transmit data some 30cm to my laptop. Since the API server is running on the same machine, data transmission is much faster than usual, leading to that race condition.
This problem was fixed with a sleep(2) call to slow down the API.
The GameStick still gave the OpenTextureFile error with no hint about the file it tried to read.
I tried my luck, built a 2058 firmware with adbd and did get a more verbose error:
I/marmalade( 5452): PJException error has occurred : 97 c:/_work/dev/marmalade_main/menu_head/src/PNGFile.cpp OpenTextureFile open png Error : Cannot open file raw:///data/GameStickCache/Assets/textures/placeholder3.png
That was very strange. I knew that the firmware image contains nearly 1000 files in the /data folder, and confirmed that this particular file also exists in both 2058 and 2071 firmware files.
It turned out that /data/GameStickCache/Assets/ was completely missing on my device, as well as /data/GameStickCache/Resources/. What the heck?
Now I remembered that when flashing firmware via system recovery, I always do two steps to get a nice clean system:
But the GameStick firmware installation process already populates the user data partition /data/! Wiping user data after installation removes all the asset files that are needed by the console user interface, leaving a broken system.
Now it also made sense that I did not see this problems on the emulator and the CFW setup: I knew those files were missing and had manually copied them from the firmware update into /data.
Day 9 had finally brought relief.
The key takaway is that the PlayJam developers did not follow standard Android conventions and put necessary system data onto the user partition.
When flashing a GameStick, always wipe user data first, and then flash the firmware .img file. Never the other way round.
Published on 2023-06-19 in bigsuck, gamestick