Android 12’s Fabricated Overlay API brings back rootless themes
The full stable Android 12 release is right around the corner, and Google has even posted the source code to its AOSP repo. There’s a lot that’s new in Android 12, including an addition to resource overlays called Fabricated Overlays. What was meant as an API to help the system manage the dynamic changes used in Material You and monet may turn into something much bigger — at least until Android 13 releases.
Mishaal Rahman discovered this new API and brought it to my attention. He had been using the shell command for it to test out different resource values in Android 12 without having to manually compile overlay APKs, and he thought it might make for an interesting app idea for rooted devices. When he brought it to my attention, I took a lot at the source code for Android 12 and noticed something that I thought was pretty interesting. I tested what I found, and now here we are — as it turns out, the Fabricated Overlay API can be used to bring back rootless themes. Before I get too far into what’s happening here, I’ll explain what Fabricated Overlays actually are.
What are Fabricated Overlays?
Fabricated Overlays are a new feature introduced in Android 12. They’re similar to the classic Runtime Resource Overlays (RROs) that Android has had for a few years now. Both RROs and Fabricated Overlays can override different resources for different applications. You can change a boolean from false to true (or vice versa), set how big you want the status bar to be, and so on.
Fabricated Overlays do have some notable differences from RROs, though. For one, you don’t need to generate an overlay APK and then install it. Instead, you just tell Android which values you want to change for which application and it takes care of registering your changes as an overlay you can then enable.
They’re also a bit more limited than RROs. Before Android 11, RROs could override pretty much any resource: booleans, integers, dimensions, attributes, layouts, and even raw data files. Android 11 made some changes to how RROs work, making overriding layouts not really feasible anymore, although it did make RROs more stable overall.
Fabricated Overlays, on the other hand, can only override values that can be represented as integers. That includes integers (duh), dimensions, booleans, and colors. You can’t use them to override raw data resources, layouts, strings, or arrays — at least not easily. This is somewhat an arbitrary limitation in the API: it only accepts integer values and resource categories as defined by the TypedValue class. TypedValue does support strings and the other resource types, but only to reference their resource, not hold their actual data.
However, those limitations aren’t too big a deal for the intended purpose of Fabricated Overlays: Material You and monet effects. Fabricated Overlays make it easy for the system to generate and apply color and dimension overlays on the fly, without having to reboot or wait for an APK to be compiled.
Now, normally, this would just be another neat API for people with rooted devices to take advantage of. Unless there’s a manufacturer-created loophole (like the one Synergy takes advantage of on Samsung devices), overlays can only be installed by third parties with root access. That’s the best part, though — Google forgot to patch a hole in Android 12.
Fabricated Overlays Without Root
Android 8 introduced the new Overlay Manager Service (or OMS) API, and people pretty quickly discovered that overlay APKs could be installed as normal apps and then enabled using ADB. Sadly, Google patched this in Android 9, and since then, only overlays signed with the same key as the system can be installed dynamically.
As it turns out, Android 12’s Fabricated Overlays have a loophole reminiscent of the one present in Android 8: they don’t need root access or signature-level permissions. They just need something running as the shell user (i.e., ADB) to register them.
It’s pretty clear that Google intended for Fabricated Overlays to only be accessible to the root and system users. There’s an ADB command implementation for creating them, and it won’t run if the executing user isn’t root. The loophole is that the check is only in the command, not the actual API, which means we can take advantage of it with a bit of work.
For a long time now, Android has had a wireless ADB feature. This lets a computer (or anything with an ADB binary and network access) connect to a device wirelessly. It’s mostly intended for Android devices that don’t have user-accessible USB connections, like smartwatches and TVs. Furthermore, before Android 11, you needed a wired ADB connection to activate the wireless mode.
Android 11 is what officially brought wireless ADB to phones and tablets. It’s a little more complicated than the classic wireless ADB, with pairing and authentication codes, but it can be activated by the user completely on-device, as long as the device is connected to WiFi. That means it’s possible to connect to your device through ADB from your device, and all you need is a WiFi connection.
Using Elevated APIs in an App
There are a lot of reasons you might want to use restricted APIs in your app. Usually, it’s because they provide some special functionality you need. As long as the API you need has a shell command implementation, it’s pretty easy to use it from an app. All you need to do is create a shell process as root (or ADB), run the right command, and parse the result, if any.
What if the API doesn’t have a shell implementation, or the shell implementation is missing something you need? If you’re on a rooted device, you can use something like libRootJava. libRootJava lets you interact with the Android framework APIs as if your app is running as the root user. This is both more convenient and much faster than running shell commands, since it’s all in the same language, and you don’t have to worry about parsing strings manually. It does have some limitations, but for the most part, it works great.
The libRootJava API is pretty flexible. You could adapt it to run as the shell user instead of root. Thankfully, you don’t have to, because someone already made it, and it’s called Shizuku. Shizuku is almost like a combination of Magisk Manager and libRootJava.
The Shizuku Manager app guides you through setting up a process running as the shell user that Shizuku can access. The Shizuku API library can be implemented into apps to let them access system APIs as if they were the shell user. It’s a much more centralized process than libRootJava, since Shizuku only needs to be set up once before every app implementing the Shizuku API library can use it. If you’re interested in how Shizuku works and how you can integrate it into your app, I have a guide for that here.
Shizuku and Fabricated Overlays
By now, you can probably see where this is going. We can use a service like Shizuku to access the Fabricated Overlays API as the shell user, and we can use Android 11’s wireless ADB feature to get shell-level access, all on-device. Since the root user restriction is only present in the Fabricated Overlays shell command and not the actual API, running as the shell user is enough to use it directly.
Implementation: Library and Sample App
What about the implementation details? Well, I’ve got you covered for that, too.
In preparation for this, I made both a library and a fully functional sample app using that library.
The library itself is mostly for convenience. It wraps some of the hidden system APIs and gives you a few convenient methods for handling Shizuku permissions. It’s also flexible, so you can provide your own instance of the IOverlayManager API if you have another way to retrieve it.
The sample app shows how you might go about implementing the library using Shizuku. It’s also a fully functional and useful app. The main page displays currently registered Fabricated Overlays that were created through it, grouped by target app. You can enable, disable, and delete them from there too.
Tapping the “Add Overlay” button at the bottom leads you to a list of all overlayable apps. Search or scroll to find the one you need and tap it. Then you can press the “Add” button at the bottom of the screen to view the list of resources that can be overridden in that app. Select a resource, set its value, and repeat for as many values as you want to change. Hit the “Save” button, enter a name, confirm, and you’ll be brought back to the main screen, now showing the new overlay, ready to be enabled.
Here are some screenshots from the app, thanks to Mishaal Rahman.
As a side note, I do also have a general overlay manager app called… Overlay Manager. The precompiled app itself is only available on my Patreon, but the source code is freely available to anyone who wants to compile or modify it.
The new Fabricated Overlays API in Android 12 is pretty great, mostly because it doesn’t need root. It may not be as sophisticated as a full-on RRO APK, but it gives you a lot more flexibility without root access.
If you’ve got a device running Android 12 and you want to give this a try, check out the GitHub repository linked above. The Releases section will have an APK to download and use. The library should be easy to include in your own application using JitPack.
Of course, you shouldn’t expect this feature to stick around for long. Google really doesn’t like third-party overlays, so this will almost definitely be fixed when Android 13 releases. In the meantime, though, enjoy it while it lasts!