“Today we have a guest post from CyanogenMod contributor, Cyanogen Inc. employee, and co-author of the new CM Theme Engine to provide a brief overview of how the Theme Engine works. Please welcome Clark Scheff. – ciwrl”
CyanogenMod makes it easy to quickly customize & change the look of your UI, allowing you to really make your device your device. As someone that loves to tinker, I like to know how things actually work and I wanted to explain how themes actually work in CyanogenMod.
At the most basic level, themes are simply a mechanism to allow resources to be replaced at runtime (as opposed to compilation time – the time when the build is created on the buildbots).
I’ll get into more details about resources, but for now just think of them as the elements which make up the application’s UI. Whenever an application is started, Android loads up the resources associated with that app and the application makes requests for these resources.
That’s the normal flow of retrieving resources, but what happens when a theme is applied? When the system loads the resources for the application, it checks if there is a theme applied and if there is, it adds the themed resources to the original resources. This is the point at which the magic happens. When the app requests a resource, the system will check if there is a themed version of it and if so, returns the themed resource, and if not it simply returns the original. The key point here is the original resources are never modified/moved/altered, the system simply returns a resource from the theme instead of the original. As you can see in the diagram below, there is no difference as far as the application is concerned.
I’m sure by now you’ve realized that this process doesn’t actually involve any actual magic, so how does the system know which themed resource to replace an application resource with?
This is done by leveraging a framework known as Runtime Resource Overlays, which was contributed to AOSP by Sony in 2014. Part of the RRO framework is a tool called IDMAP. IDMAP inspects the resources in an application and compares the resource types and names to those in the themed resources. For those resources that match it stores a mapping of the original resource to the matching resource in the theme. Once this process is complete we are left with an efficient way to determine if a resource is themed and where that resource resides.
Okay I’ve put off getting into more detail about resources long enough so let’s dive in.
Resources can range from images such as JPEGs or PNGs, XML that defines how things should be laid out on the screen, XML that define various color values that can be used to color text or highlight buttons, and even more XML that describe animations. The list goes on but there are a couple of resources that warrant a bit more explaining. One of those is the nine patch image.
A nine patch is a PNG image that has special markers around the borders that tell Android how it can stretch and place content within the image. Because analyzing those borders at runtime would take a bit of unnecessary processing, the image is processed and the border is removed and encoded into the image at build time. Android can then quickly read that information and know exactly how to handle the image. Android also likes to have all the plain text XML that the application developer has written converted it into a more efficient binary format that can be read a lot faster at runtime. The final step that needs to be done to the resources is to create a special file that indexes all of the applications resources so that they can be quickly retrieved by the android framework. Fortunately this is not a manual process, as there is a tool called the Android Asset Packaging Tool, AAPT for short.
Now that we’ve got an understanding of how themes work at the framework level, and we know what resources are and what makes Android resources so special, let’s look at what makes our Theme Engine so unique. With the original implementation of the RRO (from Sony), for every application that you wanted to theme you needed to create a theme just for that app. Sony’s method also doesn’t account for items like changing fonts, or some of the XML elements – it is primarily just overlays. We wanted theme designers to be able to theme multiple applications for many more elements, just like they were able to do in the legacy theme engine from T-Mobile.
Every application has a unique name, known as the package name, that identifies it. Fortunately Android has one place, an assets folder, that allows us to put all sorts of files and even create directories that can be accessed later by the application and by using directory names that match the unique package name of an application, a theme designer can include resources for many applications, all within one theme. This was great but there was one problem. Anything you place in these locations does not get processed like all the other resources do when an application is be built. This meant we either needed to require the theme designer use special tools or go through a painstaking process just to get the resources created in the format Android likes, or we could let them easily drag and drop their themed resources into these folders and let the device do a bit of the heavy lifting at install time. This is a very simple and convenient way for someone to create a theme without jumping through a lot of hoops, but that doesn’t mean a nice tool to create an efficient theme out of the box is out of the question ;). Letting the resources remain in their original format means we had to come up with a way to keep Android happy and not lose any of the efficiency that RRO provides. To accomplish this we actually have a version of aapt that runs on the device, and is part of every CyanogenMod build. When the theme is installed a special service goes through and creates the indexed resource file as well as processes the nine patch images and the XML. We store this file on the device and that is what provides the themed resources that can be attached to an application at runtime.
There is one last topic I want to cover before I wrap things up. Because we are allowing other designers to provide their themes to all the users, we had to make sure that themes were kept under control. This meant we had to make sure some resources just could not be themed – things like textual resources or resources that provide the UI layouts, to name two of the big ones, should not be themed. A malicious theme could easily replace the text of an app to make it appear like another app, aka spoofing. An app could get updated and changed their layout and an old theme that is theming that layout could end up crashing the app. We also have to make sure that if a theme omits some important resources or parts of resources (eg. an incomplete theme), that we bring those in from the original Holo/Material themes.
We have a lot going on under the hood to try and maintain the peace between applications and the themes that give them a fresh coat of paint. And just in case something does cause an app or apps to misbehave, we have a mechanism in place that will monitor for this and return the user to the default system theme so they can continue to use their device.
I believe our journey today has come to an end so I leave you with these parting words. CyanogenMod is community driven and all this cool stuff I talked about has been provided to the community as open source! If you have ideas for improvements, find bugs/fixes or just want to take a look at the code, everything is available on the CyanogenMod Github. If you are interested in creating a theme, checkout the Theme Template to get started!
“Awesome stuff! We’d like to thank Clark (|0xD34D|) for taking the time to give us a detailed overview. If you are a fan of this type of post, let us know in the comments – we’ll try and get a more holistic approach to content on this blog. -ciwrl”