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.
One of the few OUYA games that my ouya-plain-purchases Xposed module did not unlock was Final Fantasy III: Its developers wrote code that makes the game crash when a running Xposed module is found.
Now nearly two years after my investigation, OUYA Saviors member ZacharyFoxx found a solution: Convert the Xposed module from a normal user-installed app into a system app.
The only difference between them is that user applications are installed in /data/app/, while system apps are located in /system/app/. The /system/ partition is mounted read-only on the OUYA, so it takes some more steps to move it.
If plain purchases is not already installed, use the OUYA interface and go to Make > Tutorials and install it.
If you already enabled the plain purchases module, you have to disable it at first in the Xposed settings.
Now connect your OUYA via USB to your computer and use adb to execute the following steps:
$ adb shell
$ su
$ mount -o rw,remount -t ext4 /dev/block/platform/sdhci-tegra.3/by-name/APP
$ cp /data/app/de.cweiske.ouya.plainpurchases-1.apk /system/app/
$ rm /data/app/de.cweiske.ouya.plainpurchases-1.apk
$ mount -o ro,remount -t ext4 /dev/block/platform/sdhci-tegra.3/by-name/APP
$ exit
$ exit
$ adb reboot
The issue can finally be closed.
Update 2023-11: One user mentioned that he had to disable the OUYA Xposed "Mod Collection" to get it working (after rebooting).
I have some old Playstation 1 controllers that I'd like to use on my OUYA gaming console. Using them on the PC works via an PSX-to-USB adapter.
Plugging them into the OUYA unfortunately did not work; they were not detected as controllers - or at all, as it seemed. A Microsoft Sidewinder DualStrike did also not work.
Attaching the PSX adapter with controllers to a PC shows the following lsusb output:
ID 0810:0001 Personal Communication Systems, Inc. Dual PSX Adaptor
dmesg shows:
usb 2-1.2: new low-speed USB device number 120 using ehci-pci usb 2-1.2: New USB device found, idVendor=0810, idProduct=0001 usb 2-1.2: New USB device strings: Mfr=0, Product=2, SerialNumber=0 usb 2-1.2: Product: Twin USB Joystick input: Twin USB Joystick as /devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1.2/2-1.2:1.0/input/input1460 input: Twin USB Joystick as /devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1.2/2-1.2:1.0/input/input1461 pantherlord 0003:0810:0001.05AC: input,hidraw2: USB HID v1.10 Joystick [Twin USB Joystick] on usb-0000:00:1d.7-1.2/input0 pantherlord 0003:0810:0001.05AC: Force feedback for PantherLord/GreenAsia devices by Anssi Hannula <anssi.hannula@gmail.com>
Input device files in /dev/input/ get created for both joysticks; /dev/input/js0 and /dev/input/js1.
I can then use jstest or jstest-gtk to test the controllers and see all the buttons and axes working.
Using adb to open a shell on the OUYA gives the following dmesg output:
[ 331.002934] usb 2-1: new low speed USB device number 13 using tegra-ehci <6>[ 331.039183] usb 2-1: New USB device found, idVendor=0810, idProduct=0001 <6>[ 331.046013] usb 2-1: New USB device strings: Mfr=0, Product=2, SerialNumber=0 <6>[ 331.053437] usb 2-1: Product: Twin USB Joystick]]>
That was all. Nothing more - no more dmesg output, no input files in /dev/.
On the internet, many people used apps like usb/bt joystick center or tincore keymapper. The first has been removed from the google play store (I did not find an explanation why; the author did not respond to my e-mail), the second is still available.
But I did not want to use any of these tools. I can't look into them and do not know what they do. There just had to be a way to make it work properly.
After some detours, I had the idea that Linux drivers could be missing.
The PC's dmesg output gave a hint which ones were used:
pantherlord 0003:0810:0001.05AC: input,hidraw2: USB HID v1.10 Joystick
The keywords are pantherlord, input and hidraw2.
input is a standard kernel module that is always enabled; otherwise no mouse/keyboard/joystick/touchpad would work.
pantherlord turned out to be the driver for the PSX adapter. The kernel sources tell us in drivers/hid/Kconfig:
config HID_PANTHERLORD tristate "Pantherlord/GreenAsia game controller" depends on USB_HID ---help--- Say Y here if you have a PantherLord/GreenAsia based game controller or adapter.
There is also a HIDRAW driver, which supports
HID devices (from the USB specification standpoint) that aren't strictly user interface devices
One can access the current kernel configuration at /proc/config.gz by copying from the OUYA:
$ adb pull /proc/config.gz $ zless config.gz
And there I saw it:
# CONFIG_HIDRAW is not set # CONFIG_HID_PANTHERLORD is not set # CONFIG_HID_MICROSOFT is not set
The OUYA's Linux kernel had been built without support for my game controllers.
So it turned out that the only way to get my arcade joystick and gamepad working was to compile support for them into the kernel.
While HID_PANTHERLORD could be compiled as a module (tristate in the Kconfig files), HIDRAW could not - I had to re-build the whole kernel, and not only compile & copy a single module.
The internet contained a single, but very helpful OUYA kernel compilation howto: Builing your own Ouya Kernel by Leonardo Pires, written mid of 2014. The original announcement of this HowTo seems to be Recompilando Kernel do OUYA - Por Leonardo Pires which links to a HowTo on Google Drive. The XDA forum post seems to be a low-quality and translated version of it.
I had to adjust it to the current OUYA firmware version and fix some errors in it. Here is the updated one that worked 2015-06-02:
You have to enable ADB via the OUYA System / Debugging menu.
For the last step you have to be connected via USB to the device; uploading the kernel via network does not work.
Create a udev rules file for the OUYA so that you are able to communicate with the device over USB. Put the following into /etc/udev/rules.d/051-android.rules:
# ouya SUBSYSTEM=="usb", ATTRS{idVendor}=="2836", MODE="0666" # fastboot ouya SUBSYSTEM=="usb", ATTRS{idVendor}=="0955", MODE="0666"
Install the Android debug bridge (adb) and make it recognize the OUYA as Android device:
$ sudo apt-get install android-tools-adb
$ echo "0x2836" >> ~/.android/adb_usb.ini
$ adb kill-server
$ adb devices
List of devices attached
012d3a4efac56789 device
In your home directory, create a directory ouya-kernel:
$ cd
$ mkdir ouya-kernel
$ cd ouya-kernel
Flashing the OUYA with a new kernel requires a ramdisk file, and we can get it from the current firmware. At the time of writing this was 1.2.1427-r1.
$ wget http://devs-ouya-tv-prod.s3.amazonaws.com/ota/RC-OUYA-1.2.1427-r1_ota.zip
$ unzip RC-OUYA-1.2.1427-r1_ota.zip boot.img
$ wget http://www.enck.org/tools/split_bootimg_pl.txt -O split_bootimg.pl
$ perl split_bootimg.pl boot.img
$ gunzip boot.img-ramdisk.gz
$ mv boot.img-ramdisk ramdisk
split_bootimg.pl's homepage is http://www.enck.org/tools.html#split_bootimg.
We have to cross-compile the kernel for the ARM platform; and the NDK contains the correct tools for that.
android-ndk-r10e did not work for me; I got a unknown CPU architecture error during kernel compilation. android-ndk-r9d did work:
$ wget https://dl.google.com/android/ndk/android-ndk-r9d-linux-x86_64.tar.bz2
$ tar xjvf android-ndk-r9d-linux-x86_64.tar.bz2
OUYA's kernel is on github, and we fetch it from there:
$ git clone git@github.com:ouya/ouya_1_1-kernel.git
Now we have to fetch the OUYA's kernel configuration file, which can be found at /proc/config.gz:
$ adb pull /proc/config.gz
$ gunzip -kc config.gz > ouya_1_1-kernel/.config
Now adjust the kernel configuration file in ouya_1_1-kernel/.config:
CONFIG_HIDRAW=y CONFIG_HID_MICROSOFT=y CONFIG_HID_PANTHERLORD=y
The kernel can be compiled now:
$ export CROSS_COMPILE=/home/cweiske/ouya-kernel/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/arm-linux-androideabi-
$ export ARCH=arm
$ cd ouya_1_1-kernel
$ make
.. wait an hour ..
$ cp arch/arm/boot/zImage ../
Install the fastboot command:
$ apt-get install android-tools-fastboot
$ adb reboot-bootloader
$ fastboot devices -l
012d3a4efac56789 fastboot usb:2-1.2
If fastboot gives you a no permissions warning, make sure that /etc/udev/rules.d/51-android.rules contains settings for vendor 0955 - the USB IDs change when rebooting into bootloader mode!
Now it's time to install the new kernel:
$ fastboot flash:raw boot ./zImage ./ramdisk
creating boot image...
creating boot image - 5742592 bytes
sending 'boot' (5608 KB)...
OKAY [ 1.125s]
writing 'boot'...
OKAY [ 1.854s]
finished. total time: 2.980s
$ fastboot reboot
If all went well, the OUYA boots up with the fresh kernel.
dmesg on the OUYA shows us that the joysticks are recognized now:
[ 113.056632] usb 2-1: new low speed USB device number 2 using tegra-ehci
<6>[ 113.104851] usb 2-1: New USB device found, idVendor=0810, idProduct=0001
<6>[ 113.116743] usb 2-1: New USB device strings: Mfr=0, Product=2, SerialNumber=0
<6>[ 113.124579] usb 2-1: Product: Twin USB Joystick
<6>[ 113.145452] input: Twin USB Joystick as /devices/platform/tegra-ehci.2/usb2/2-1/2-1:1.0/input/input1
<6>[ 113.156314] input: Twin USB Joystick as /devices/platform/tegra-ehci.2/usb2/2-1/2-1:1.0/input/input2
<6>[ 113.167024] pantherlord 0003:0810:0001.0001: input,hidraw0: USB HID v1.10 Joystick [Twin USB Joystick] on usb-tegra-ehci.2-1/input0
<6>[ 113.179213] pantherlord 0003:0810:0001.0001: Force feedback for PantherLord/GreenAsia devices by Anssi Hannula ]]>
The Playstation controllers did not work immediately. The axes (up/down, left/right) did work, but the keys did not. I had to provide my own key layout file for the joysticks.
Creating it was easy; I installed the KeyTest Android application (link is dead) which could be downloaded as pre-compiled .apk file (archived version).
Then I clicked all the buttons on the OUYA controller and wrote down the scan codes. Afterwards, I wrote down the scan codes of the Playstation controllers.
The resulting key layout file has to get the name Vendor_0810_Product_0001.kl.
# PSX controller mapping # Dual PSX Adaptor / Twin USB Joystick # idVendor=0810, idProduct=0001 # Linux HID_PANTHERLORD driver # X cross key 290 BUTTON_A # O circle key 289 BUTTON_B # [] square key 291 BUTTON_X # /\ triangle key 288 BUTTON_Y key 294 BUTTON_L1 key 292 BUTTON_L2 key 295 BUTTON_R1 key 293 BUTTON_R2 key 296 BUTTON_SELECT key 297 BUTTON_START key 298 BUTTON_THUMBL key 299 BUTTON_THUMBR axis 0x00 X axis 0x01 Y axis 0x02 HAT_X axis 0x03 HAT_Y axis 0x04 RZ axis 0x05 Z
Push it onto your OUYA into /system/usr/keylayout/:
$ adb push Vendor_0810_Product_0001.kl /sdcard/
$ adb shell
$ su
$ mount -o remount,rw /system
$ mv /sdcard/Vendor_0810_Product_0001.kl /system/usr/keylayout/
Now unplug and replug the USB controller - and it works!
My OUYA community server is running since 2019-11, and in 2021 I compiled the usage statistics for 2020. Time for the 2021 and 2022 numbers!
Year | Bootups |
---|---|
2020 | 10 398 |
2021 | 11 381 |
2022 | 14 658 |
Year | Initial logins |
---|---|
2020 | 733 |
2021 | 673 |
2022 | 858 |
My own OUYA API server has been running since november last year. The game data files have been worked on: Previously missing games were added, wrong developer UUIDs fixed. Many of the games got product data, so that purchases can be restored now and games stuck in demo mode unlock to the full version now.
All went nicely until end of january, people with factory reset OUYAs came into the chat and asked for help because they could not get past the network configuration screen. All they got were the following error messages:
Unable to connect
Please check your Internet connection and try again.
and
Cannot establish a connection with the OUYA Servers. To turn on Wi-Fi, unplug your Ethernet cable and proceed. To retry ethernet, please check your connection and then unplug and re-plug your cable.
Luckily I own two OUYAs, so I did a factory reset on the development machine and tried the setup myself: It failed with the same error messages as reported.
The initial setup is managed by OUYAOOBE.apk, the Out Of the Box Experience application. I read the decompiled sources and soon found the culprit, ConnectivityChecker.java:
public Void doInBackground(Void... params) {
Socket socket = null;
boolean reachable = false;
try {
Socket socket2 = new Socket("devs.ouya.tv", 80);
reachable = true;
if (socket2 != null) {
try {
socket2.close();
Socket socket3 = socket2;
} catch (IOException e) {
Socket socket4 = socket2;
}
}
} catch (UnknownHostException, IOException e2) {
// error handling
}
setResult(reachable);
return null;
}
So OUYA's setup application has a hard-coded check to devs.ouya.tv, which was the name of the API server - and it does not use the ouya config override file ouya_config.properties. The DNS record still exists:
$ dig devs.ouya.tv devs.ouya.tv. 60 IN CNAME tokyo-6663.herokussl.com.
But the host that the domain was aliased to does not exist anymore:
$ dig +short tokyo-6663.herokussl.com $
Apparently Razer's Heroku contract was cancelled or not renewed, and the box that served the redirect from devs.ouya.tv to razer.com was shutdown forever.
It is possible to get past the network selection screen by letting the OUYA think that devs.ouya.tv still exists, and let it point to some IP address that has a HTTP and HTTPS server running.
Choose one of the following options:
Summary: Temporarily change the OUYA's network configuration to use 178.254.13.17 as DNS server.
Some routers allow you to add own domain-name-to-IP-mappings. If your router is one of them, add a mapping for hostname devs.ouya.tv, status.ouya.tv and ouya-updates.s3.amazonaws.com to the IP of my server: 178.254.13.17.
Each computer has a file /etc/hosts that allows you to define DNS mappings. On Android, that file is located at /system/etc/hosts.
This method will surely work, but takes the most steps. You need to have the "Android Debug Bridge" (adb) command line tool installed on your PC. Have a look at Cyanogen Mod CM11 and the Ouya, it has installation instructions for adb on windows.
adb shell
su
to become the root user
mount -o rw,remount -t ext4 /dev/block/platform/sdhci-tegra.3/by-name/APP
echo "178.254.13.17 devs.ouya.tv status.ouya.tv ouya-updates.s3.amazonaws.com" >> /system/etc/hosts
The connectivity check should work now without rebooting.
If you still get the "Unable to connect" error, then you are missing the ouya_config.properties file.
Because I am moving to a new server, the IP address changed from 83.169.45.222 to 178.254.13.17.
I collected a list of all games that were exclusively released on the OUYA gaming console.
Games that have never been ported to any other system - you need an OUYA to play them. Find them in the Exclusive category on my OUYA server.
Games that were for some limited time only available on the OUYA, but got released on other platforms later. Find them in the VIP room category on the OUYA server.
Game name | Other Platforms | Source | Notes |
---|---|---|---|
Amazing Frog | PC (Steam) | 15, 17, 18 | |
Broken Age | PC (Steam), Playstation, Apple App Store, Google Play | 1 | Only console on launch day |
Chrono Blade | Apple App Store, Google Play | 14, 15 | Demo only |
Deep Dungeons of Doom | OUYA (2013-04-11), Apple App Store (2013-06-04), Google Play | 15, 19 | |
Duck Game | OUYA (2014), Nintendo Switch, Playstation 4, PC (Steam) (2015) | 11, 13 | new official site |
Laza Knightez | Windows, Mac OS | 6 | |
No Brakes Valet | Windows, Apple App Store, Google Play | 15, 16 | new official site |
Polarity | PC (Steam), Apple App Store, Google Play | 1, 15 | new official site |
Reagan Gorbachev | Xbox One, PC (Steam, Humble Store) | 2, 11 | |
That Dragon, Chancer | Mac, PC, Google Play, Apple App Store | 4, 22 | new official site |
Toto Temple | Playstation 4, Xbox one, WiiU, PC (Steam, Humble Store) | 7, 8, 11 | |
Towerfall | Switch, PC (Steam), Playstation 4, Xbox One | 9, 10, 14, 15 | |
Whispering Willows | Switch, PC (Steam), Playstation 4, Xbox One, Wii, Google Play, Apple App Store | 11, 12 | 2 months exclusively on OUYA according to 12 |
Some games were announced to be exclusively on OUYA but weren't actually, or were cancelled.
Game name | Source | Notes |
---|---|---|
Cascade | 11, 23 | Never released on the OUYA store. No mention of OUYA on homepage. |
Fist of awesome | 15 | Kickstarter page says PC & Mac were there at least with ouya. |
Human Element | 1, 24 | Cancelled |
Neverending Nightmares | 11 |
Released simulatenously on Windows, Mac, Linux, and Ouya.
Kickstarter page:
The Ouya Free The Games Fund exclusivity terms don't apply to computers. |
PitchWreck | 5, 25 | Never released |
Red. | 15, 26 | Released the same time on iOS and OUYA, 2013-03-13. |
Yummy Circus | 1, 3 | Not on the ouya.tv games list. Was listed on their website as available on OUYA. |
The OUYA gaming console amassed over 1200 games in its online store, most of which have been preserved on the Internet Archive and made available to OUYAs via the community server ouya.cweiske.de.
Other companies thought it would be great if their Android TV consoles would have access to this large library of games, and so OUYA Inc. announced the "OUYA Everywhere" program in 2014-03. The first partner was Mad Catz with its MOJO microconsole, and with firmware update MO0205-TS in 2014-06 they shipped the OUYA Everywhere installer and put it onto the Mojo's desktop:
Forward 8 years later, and Ryo on the OUYA saviors discord chat server tries to get the OUYA community server working with his Mojo but fails in the end. User Toast offers to send his spare Mojo to me so I can look into the problem, which I happily accept. A couple of weeks later it arrives and I can have a first look at it.
After a factory reset I see that the Mojo firmware does not contain the "OUYA Everywhere" software but on the "OUYA Everywhere installer" which tries to download, install and launch the actual OUYA software. The installer requests an API endpoint /api/v1/partner_builds that my documentation did not know of, and thus my server does not support.
With the help of the awesome jadx-gui I reverse engineer the JSON file format that the installer expects. The API endpoint tells the installer what to do: Install or uninstall .apk files, show a message, start an application or exit. A first test shows a popup:
Since I factory reset the Mojo, I don't have the previously downloaded ouya everywhere apk files anymore :( A XDA forum thread contains early files (version 1.2.145), and a post on page 3 the latest version 1.2.897. So I built a JSON that tells the Mojo to install the OUYA OOBE (setup), OUYA framework and the OUYA launcher packages and starts the latter.
This was all it took: A single file on my server with the correct content, and now all Mojo owners can play OUYA games again!
The Mojo must be told to use the new server by putting a file ouya_config.properties in the folder /sdcard/. Contents of the file can be found on ouya.cweiske.de.
When this is done, simply open the OUYA icon on the Mojo's desktop and the OUYA OOBE setup will greet you.
My xposed module works on the Mojo as well, so most of the games can be unlocked.
The first beta of OUYA Everywhere 1.2.145 could be installed and started on all devices, but later ones only ran on on whitelisted consoles like the Mojo. This first version has been dubbed "OUYA Anywhere" by the community.
Today when looking at the latest source code I found that the "is this device supported" check has another condition:
//tv.ouya.console.launcher.home.HomeActivity
if (!DeviceInfoReceiver.getInstance().checkIsSupportedDevice()
&& !SystemProperties.getDebug()
) {
DeviceNotSupportedDialog.show(this);
return;
}
Debugging can be enabled by putting the line DEBUG=1 into the ouya_config.properties file.
With that extra configuration line, I am able to use the OUYA launcher on my Nexus 7 tablet with Android 11:
We have some problems, though:
To get games to unlock on the defunct OUYA gaming console, I wrote an Xposed module that disables encryption for purchase receipt requests and responses that the games send and receive.
While it worked for many games, users reported that Final Fantasy III crashes when my plain-purchases module is enabled. adb logcat shows the following stacktrace:
E/AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: main java.lang.SecurityException: 1466 does not have permission:android.permission.CLEAR_APP_USER_DATA to clear datafor process:de.cweiske.ouya.plainpurchases at android.os.Parcel.readException(Parcel.java:1425) at android.os.Parcel.readException(Parcel.java:1379) at android.app.ActivityManagerProxy.clearApplicationUserData(ActivityManagerNative.java:2889) at com.android.commands.pm.Pm.runClear(Pm.java:1126) at com.android.commands.pm.Pm.run(Pm.java:116) at com.android.commands.pm.Pm.main(Pm.java:75) at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:235) at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:135) at dalvik.system.NativeStart.main(Native Method)
Final Fantasy 3 is not the only game that shows this behavior; many Square Enix games exhibit this behavior.
FF3 for Android/OUYA contains a native library lib__57d5__.so which seems to be the source of the problem. Fortunately for me, the Xposed author had a look at that library before:
Ok, whatever Final Fantasy does there: It looks like some very dirty coding.
- com/square_enix/android_googleplay/FFIV_GP/MainActivitya loads lib__57d5__.so and then calls native function MainActivityb.a(I)I with some random (?) integer constants
- Actually already the library loading fails because somewhere during the initialization, it calls sh -c pm clear <package> for the Xposed modules, which fails with a permission error and crashes the process
- [...]
To summarize: A strange native library in FF seems to get all /data/app/*-?.apk entries from the memory mapping file and tries to clear the data for them.
So the lib tries to clear the application user data for all apks that somehow linger in the memory of the FF3 game. It does not do this directly, but by opening a shell and using the android package manager command line tool pm:
$ sh -c pm clear packagename
After reading the sources of pm I came to the following call stack:
final fantasy 3 + lib__57d5__.so + sh -c pm clear $package + android.app.ActivityManagerProxy::clearApplicationUserData + ?? IPackageDataObserver - transact(CLEAR_APP_DATA_TRANSACTION) something with binder + ??? PackageManager::clearApplicationUserData() + com.android.server.pm.PackageManagerService::clearApplicationUserData (final String packageName, final IPackageDataObserver observer, final int userId)
The pm cli tool does not directly call the package manager, but - to my limited understanding - uses some Android IPC mechanism to call the one authorative instance of the actual package manager.
At first I wrote an xposed module that hooked into PackageManagerService::clearApplicationUserData and simply said "success" when the data of my own module were to be cleaned:
iPackageDataObserverClass = Class.forName("android.content.pm.IPackageDataObserver");
Class>[] paramTypes = {String.class, boolean.class};
Method onRemoveCompletedMethod = iPackageDataObserverClass.getMethod("onRemoveCompleted", paramTypes);
Object[] params = {packageName, true};
try {
onRemoveCompletedMethod.invoke(observer, params);
//observer.onRemoveCompleted(packageName, true);
} catch (Exception e) {
XposedBridge.log("Observer no longer exists.");
}
}
//end dance
}
}
}
);
}
}]]>
The unaccessible IPackageDataObserver made it a bit hard to call the right methods, but it was still relatively easy. Unfortunately, it did not work at all because it was too late: ActivityManagerProxy.clearApplicationUserData() already does the permission checks!
My goal was to hook into ActivityManagerProxy that does the IPC call to the package manager.
This proved to be extremely hard for me because hooking into command line tools with IXposedHookCmdInit is not supported anymore by Xposed: See XDA: Hook cmds (PM) and the XPosed API changelog for 2.6:
IXposedHookCmdInit is deprecated now, initCmdApps() won't be called anymore unless an undocumented file has been created. Only two modules used it, both got rid of it without any loss of functionality.
Rolling back my OUYA's XPosed from version 2.6.1 (API v31) to 2.5.1 (API v50) worked, but then I faced the most difficult problem: I could not compile the code, because the compileOnly de.robv.android.xposed:api dependency is only available for API version 53 and higher - and 52 was the last one with support for command line tool hooks :/
It took quite some time to find an older XposedBridgeApi jar file, but eventually one turned up: XposedBridgeApi-52.jar. Moving that to the module's lib/ directory let me compile the module with the cli hook! Now I faced a very strange error:
I/Xposed: Running ROM 'JZO54L-OUYA' with fingerprint 'OUYA/ouya_1_1/ouya_1_1:4.1.2/JZO54L-OUYA/1427:user/test-keys' I/Xposed: Loading modules from /data/app/de.cweiske.ouya.plainpurchases-2.apk I/Xposed: Loading class de.cweiske.ouya.plainpurchases.PlainPurchases I/Xposed: java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation at dalvik.system.DexFile.defineClass(Native Method) at dalvik.system.DexFile.loadClassBinaryName(DexFile.java:211) at dalvik.system.DexPathList.findClass(DexPathList.java:315) at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:58) at java.lang.ClassLoader.loadClass(ClassLoader.java:501) at java.lang.ClassLoader.loadClass(ClassLoader.java:461) at de.robv.android.xposed.XposedBridge.loadModule(XposedBridge.java:441) at de.robv.android.xposed.XposedBridge.loadModules(XposedBridge.java:407) at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:121) at dalvik.system.NativeStart.main(Native Method)
The source of the problem was that I moved XposedBridgeApi-52.jar into the lib/ folder, which meant it got compiled into the xposed module jar file. Now on the OUYA, there were suddenly two implementations of the bridge API, and the JVM could not decide which was the right one.
I moved it into a different directory, told gradle/Android Studio to use it for compilation only and could load the mod without problems!
iPackageDataObserverClass = Class.forName("android.content.pm.IPackageDataObserver");
Class>[] paramTypes = {String.class, boolean.class};
Method onRemoveCompletedMethod = iPackageDataObserverClass.getMethod("onRemoveCompleted", paramTypes);
Object[] params = {packageName, true};
try {
onRemoveCompletedMethod.invoke(observer, params);
//observer.onRemoveCompleted(packageName, true);
} catch (Exception e) {
XposedBridge.log("Observer no longer exists.");
}
}
//end dance
}
}
);
}
}]]>
Running pm clear ... from shell as limited user worked flawlessly and gave exit code 0 and no exceptions! Now I had beaten Square Enix! I started Final Fantasy 3 via shell:
$ adb shell am start -n com.square_enix.android_OUYA.FFIII/com.square_enix.FFIII_J.MainActivity
... and FF3 stopped as it had before:
I/Xposed: clearApplicationUserData: de.cweiske.ouya.plainpurchases D/AndroidRuntime: Shutting down VM I/AndroidRuntime: NOTE: attach of thread 'Binder_3' failed D/dalvikvm: JIT code cache reset in 0 ms (86452 bytes 1/0) D/dalvikvm: Debugger has detached; object registry had 1 entries D/Zygote: Process 1572 exited cleanly (1) I/ActivityManager: Process com.square_enix.android_OUYA.FFIII (pid 1572) has died. W/ActivityManager: Force removing ActivityRecord{420f33f0 com.square_enix.android_OUYA.FFIII/com.square_enix.FFIII_J.MainActivity}: app died, no saved state
lib__57d5__.so did not care about pm's exit code, it always stops when it detects that XPosed is running.
I'm very sure that the SecurityException was just a red herring to make people spend time with a pretty difficult problem that has nothing to do with the actual problem.
/me tips his hat to the lib__57d5__.so developers.
Update 2023-01-10: We have a solution.
With a bit of setup work, you can again play all the games you bought on your OUYA gaming console.
When the OUYA server shut down in July 2019, many users noticed that the games they paid for now went back to demo/trial mode:
So I just hooked up my ol' Mad Catz M.O.J.O., and was pretty surprised to find that a vast majority of my games that I purchased through the Ouya store no longer work, and act like I've never purchased them
(Sonic 4 episode 1, for example, just launches the "Trial" version now).
glow162, 2019-11-16 on reddit
Soul Power is one of my paid Ouya games that reverted to demo mode since the servers went down.
Would it be possible to recieve a different, working, version of the game if I show you my Ouya receipt?
Starfig the boyo, 2019-08-22 on Twitter
@x10interactive Hi! The Ouya version of So Many Me isn't working anymore, it's reverted to demo mode!
Starfig the boyo, 2019-08-21 on Twitter
Would it be possible to release some kind of patch to play Evocreo on the Ouya?
I bought a copy and no longer have access to it after the server shutdown
Chance Crane, 2019-08-22 on Twitter
@SquareEnixFR @SquareEnixMtl @SquareEnix @SquareEnixUSA
After #ouya shutdown, How can I keep playing Final Fantasy 3 as I bought it and the DRM now keeps me off ? Thanks.
A long while back, I bought the Final Fantasy 3 remake on the Ouya. I really enjoyed it and got all the way up to the final dungeon. Things happened and I wasn't able to play it for a long time.
Then Razer shut down the Ouya servers and the game stopped working.
E. Zachary Knight 2019-12-02 on Twitter
I did buy Babylonian Twins and that one also went back to demo mode.
The shutdown casualty list thread on the OUYA Forum lists many games that reverted back to demo mode.
Some developers were very gracious and published a final game update before the store shut down that removed the DRM code and made the game fully free to play.
List of games I know got a final unlock release:
I'm very thankful for them doing that.
Other developers could not do that for various reasons. I contacted the developers of Bloo Kid 2. They would like to have helped me, but the machine that was used to develop and compile Bloo Kid 2 for Android/OUYA crashed and could not be restored :/
OUYA's own Purchasing documentation describes how purchases happen on the OUYA:
OUYA's creators wanted to make it very hard to tamper with the purchase process. Purchase requests are sent encrypted to the server, and receipt responses are encrypted twice.
For that they needed two kind of key pairs:
When buying a game, the purchase request is AES-encrypted. The AES key itself is encrypted with the game's public key. The store server has the private game key and can decrypt it.
The receipt list is encrypted twice: Data themselves are encrypted with the public gamer key, and the console has the private key to decrypt it. The second "inner" encryption was made with the private developer/game key, and the in-game public key is used to decrypt it (that is reverse to what private and public keys are normally used, but it works somehow).
The problem for my own OUYA server is that I do not possess the private keys of all of the 1200 games. I am not able to decrypt the purchase requests, and not able to encrypt the receipt lists in a way that the game can decrypt them.
I might not able to break purchasing, but I could unlock the individual games.
Bloo Kid 2 was the first game I tried. I already knew where to look for and modified isFullVersion in the smali code to always return true. Then I re-compiled the smali code, created the .apk and signed it.
But now I had the problem that my signature was different from the original signature, and Android refused to install the new game version over the old one. Even uninstalling the game while keeping the data (pm uninstall -k) did not work; Android refused to install the modified game with the new signature as long as it had data for the original signature :/
This would mean that people would lose all their progress and settings.
Comment by Jesús Higueras: At least on regular Android devices you can run pm uninstall -k and then restart the device. The data will still be there but the signature will no longer be cached, so you'll be able to install your modified version on top without losing data.
Each game has a "purchase success" and an equal "purchase failure" handler. My idea was that upon game start, I would call the purchase success code with the right product key and be done with it.
I did that, and the game crashed - inside the C code that I could not inspect so easily. I even installed Ghidra to be able to look into the C code.
It took me three evenings to get the game to the point that it actually worked. The solution was to call the purchase success method from the purchase failure handler :) When calling it directly on startup, some things in the C code were not yet initialized and that caused the crash.
Here again I faced the impossible-to-upgrade and the keep-my-savegames problem.
I stopped the work because patching individual games does not scale.
The OUYA saviors very early had the idea to use Xposed to overwrite OuyaEncryptionHelper methods and prevent them from doing any encryption/decryption.
Unfortunately this helper is compiled into every application, and more often than not obfuscated with methods names like a.a.a.d.c. We would need a list that mapped that class name for each of the games, which would mean a lot of manual work.
While reading through the decompiled OUYA Framework source code and the game-specific implementations I saw something:
Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
instance.init(2, secretKeySpec, ivParameterSpec);
receipts = new JSONObject(new String(instance.doFinal(decode), "UTF-8"));
The framework and every game used Java's Cipher.doFinal(byte[] input) to actually execute the encryption or decryption operation. If it was possible to let doFinal() return the input bytes unmodified, then my server would get plain text purchase requests and could return plain text receipts!
It took a whole day of installing Xposed on the OUYA, learning how to build an Xposed module and write a pre-execution hook for Cipher.doFinal. The actual code is disappointing:
XposedHelpers.findAndHookMethod(Cipher.class, "doFinal", byte[].class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
byte[] input = (byte[]) param.args[0];
param.setResult(input);
}
});
The result was this:
And all this without modifying the game apk itself! The savegames are safe!
Using my "plain-purchases" xposed module currently takes a bit of work:
Follow the Root the OUYA instructions until you have the following:
(Both are also available in Make -> Tutorials).
Do not install ModCollection nor Google Play (they are not needed, but do no harm when installed).
You may now enjoy all your OUYA games in their full glory again, half a year after the official server shutdown.
To unlock Final Fantasy III, the module needs to be installed as a system app.
If you are a game developer and do not want your game unlocked on the couple of dozen OUYAs that are left on the world, send me an e-mail and I will remove the purchase receipt from my server. The game will then fall back to demo mode on all OUYAs.
Also on: Twitter, ouya.world.
The OUYA gaming console desperately wants to talk to a server. Without a server many things do not work: Registration during setup, detail pages of installed games and the whole "Discover" store.
When Razer bought OUYA they promised to keep the server running for a year, and in 2019-06 they finally shut them down.
My community replacement server went online in 2019-11 and has been running since - but nothing is forever.
To prepare for the unavailability of my own server in the far distant future, I built an Android app that acts as an OUYA API server and that you can install on your OUYA: louyapi, the local OUYA API.
[API] | | | | | | | | | | | | | `-------------´ +----------+]]>
Your OUYA talks to itself, not needing internet to register a new user or to show game details.
Only game images and .apk downloads still need internet access, because they are over 100 GiB in total.
On your OUYA, simply go to Discover -> App and find and install louyapi from there.
In your browser, you can download louyapi from my server or from Github.
I wanted to upload it to the Internet Archive, but they rejected the .apk file :(
Start the user interface from the Play menu. It will show the current game data version and lets you switch the OUYA to the internal server with a single click:
Once you've done that, exit the app. It will keep running in the background to answer requests from the OUYA user interface: