To understand how APKs are ran on Android devices, we got to first understand Android’s structure. Android platform architecture can be defined as the following:
The Android platform is built on top of Linux kernel which provides low level drivers and functionality needed to operate a mobile device such as power & management and threading.
On top of that, HAL provides access to hardware such as Camera and Bluetooth sensors through shared libraries exposed via the Java API Framework which applications can use. The layer serves as an abstraction to the rest of the OS so it can remain agnostic to implementation of hardware which is usually taken care of by device vendors.
For the scope of running 3rd party apps, we’ll be mainly looking at the Android Runtime, which handles running the compiled .dex
(Dalvik bytecode) file(s) inside of APKs.
Installation
When a user installs an APK into their device, and assuming the device is using ART (Android RunTime), which is made default on Android 5 and above, the app is actually further compiled ahead-of-time (AOT) in to another format called OAT by an on-device tool nameddex2oat
.
For the adventurous, the paper — ARTist: The Android Runtime Instrumentation and Security Toolkit, provides an interesting and in-depth look at the inner workings of
dex2oat
tool.
This is done so that no further compilation is needed at runtime and execution by ART will be much quicker since it only deals with native machine code compared to the platform agnostic DEX format. Using code not compiled ahead-of-time would mean it had to be either compiled in runtime (JIT) or interpreted; which are usually much slower.
Newer versions of Android combines the approach into a hybrid model as described here. However, to keep things simple, we’ll refer to ART as AOT compilation method only in this article.
Technically speaking, the output of dex2oat
is an ELF formatted file despite what its extension or naming would otherwise suggest. The specialized OAT file exist underneath the ELF format wrapper. The exact format of OAT files defer from various versions of Android but the idea is that it contains compiled native/machine code for a specific hardware architecture and can be executed directly without interpretation. I recommend this article to learn more about the ELF and OAT formats.
The ELF format makes good sense as its used in Linux for assembly code and is compatible with system tools such as linker in Bionic. More information on Bionic and its linker here
Aside from the native machine code, the OAT file usually also contains the original DEX file as a fallback or to be used in debugging. This changes in Android O though, which uses a new format,
.vdex
, to hold the DEX file(s)
Its worth to note oat
file extension naming can be confusing at times. .odex
file extension used in other articles (especially older ones) may speifcally mean the file from an older version (Dalvik runtime) of dex2opt
which is not the same format as oat file format. In the context of ART though, .odex
file extension can also be an oat file, don’t be confused when reading the official documentation!
Runtime
Whenever a new app is launched, Android starts by cloning a base process called Zygote. Zygote is created shortly after boot and contains all the needed libraries already loaded into memory. Those codes are commonly used by Android apps so its more efficient to “copy” from this process than spawning new ones from scratch for every app. The newly cloned process takes on a new process id and branch out to become the launch app’s process. Internally, the Linux kernel uses copy-on-write technique so each new process is not technically a copy per se. For more info on Zygote and Android boot process, I recommend this article which was really helpful.
Now that we have our own Android process, the runtime will then start to load the class files into memory. Recall in the APK, we have the compiled classes.dex
which subsequently got converted into oat formatted file, which was done to optimise loading and execution of codes quickly for the Android runtime. With the new process spawned, the Android Runtime can then directly load the oat’s native/machine code into the process and is ready to begin execution.
Conclusion
At this point, the CPU executes the instructions of the machine code which is beyond the scope of this article and its aim. Although this article is rather short, it showcases and introduces the components involved in launching and running an APK. It can be thought of as an important bridge or middleware ,architecturally speaking, between the packaged code and everything the user will eventually experience on the device.
Stay tuned to the next series where I will be exploring how Android renders the UI and views we see on the device. As always, the topic is open to discussion and thanks for reading!