AOSP Foundations
10 min read

Android Versions Timeline

A technical overview of Android's version history, focusing on platform and architectural milestones relevant to AOSP developers.

Imagine building a brand new operating system in 2007. Your goal is to convince developers to write apps for it, but there is a massive problem right out of the gate. Engineers have absolutely no idea what processors will actually run the software. Hardware manufacturers are actively experimenting with ARM, x86, and MIPS architectures. Compiling native C or C++ code directly for each processor would force developers to publish multiple versions of every single app. That friction alone would kill the platform before it even launched.

The Hardware Chaos of Early Android

Building an ecosystem requires scale, and scale requires hardware independence. Developers needed a way to write an app once and have it run anywhere, regardless of the physical silicon inside the phone. How do you execute code on a CPU when you do not know the CPU architecture? This constraint forced the original Android engineering team to make a critical architectural decision. They could not use traditional ahead-of-time native compilation.

The solution was the Dalvik Virtual Machine. Instead of compiling Java source code down to machine code, the build tools compiled it into an intermediate format called Dalvik Executable (.dex) bytecode. A physical device would then translate that bytecode into native machine instructions on the fly as the user opened the app.

This real-time translation mechanism is called Just-In-Time (JIT) compilation. Think of it like distributing a recipe instead of a fully baked cake. You send the exact same recipe to every kitchen, and each kitchen uses whatever oven they have to bake it. When a user tapped an app icon, Dalvik would spin up a process, read the .dex bytecode, compile the hot paths into native CPU instructions, and execute them.

Mapping the execution flow shows how the architecture completely isolates the developer workload from the final hardware execution. Standard Android build systems simply produce a .dex file. Local devices handle the final translation.

In this diagram, the developer compiles the code exactly once. Shipping the resulting DEX file to every device is the next step. A local Dalvik JIT compiler on the specific phone takes over from there. One single APK could run entirely unchanged on an ARMv7 phone and an x86 tablet in 2011.

Did You Know: A lot of engineers assume Android used Java simply because it was a popular language. The real reason was strategic. Java's ecosystem already had tooling for compiling to intermediate bytecode, which gave Android the hardware independence it desperately needed.

This architecture worked exactly as intended and helped Android capture the global market. But this cross-platform flexibility came at a massive cost to battery life and CPU performance.

The Performance Wall (Dalvik to ART)

Translating code every single time a user opens an app requires severe CPU cycles. By 2013, phones featured higher resolution screens and apps were vastly more complex. Users were noticing UI jank and terrible battery drain. The JIT approach had hit a physical limit. Spending precious processing time converting bytecode into machine code while the user was actively trying to scroll a list simply stopped scaling.

Google answered this crisis by ripping out Dalvik entirely and replacing it with the Android Runtime (ART). ART fundamentally shifted the compilation strategy. Instead of translating code on the fly at runtime, the system introduced Ahead-of-Time (AOT) compilation. The OS would compile the entire app into native machine code before the user ever opened it.

This traded storage space and installation time for pure CPU efficiency. Think of this approach like translating a book the night before a presentation and just reading the native language on stage, rather than translating it in your head while trying to speak. When a user downloaded an app, the ART dex2oat compiler immediately converted the .dex bytecode into a native ELF binary.

Comparing the two execution pipelines reveals the architectural shift. The JIT path burns CPU cycles right when the user wants performance. An AOT path pays that cost upfront.

The Dalvik JIT path blocks the application execution while it compiles the code. ART AOT completely skips compilation during the app launch. If you used an early ART device on Android 5.0, you probably remember the "Android is upgrading... Optimizing app 1 of X" boot screen. That was AOT compilation happening for every single app during the system boot.

Warning: A common trap is thinking ART means apps are no longer written in Java or Kotlin. Source code remains exactly the same. Build systems still output standard DEX bytecode. Only the timing of when that DEX gets compiled into native machine code on the device actually changes.

These compilation changes fixed the core performance issues, but a new systemic threat emerged. Device manufacturers were taking months to deliver these new system updates, if at all.

The OS Update Crisis (Project Treble)

Getting a new version of Android onto a phone used to be an organizational nightmare. A monolithic build heavily tangled the Core Android Framework code and proprietary hardware drivers together. If Google released Android 8.0, hardware manufacturers had to completely rewrite and retest their camera, audio, and GPU drivers to work with the new framework. Why did a simple OS update break the camera drivers? The framework and the hardware layers shared direct functional dependencies, meaning any change to the core OS required rebuilding the proprietary binaries. This process was so expensive and slow that many phones just never received updates.

Google introduced Project Treble in Android 8.0 to stop this bleeding. Treble shifted Android from a monolithic architecture to a highly modular one. The initiative formally separated the OS framework from the vendor hardware implementation.

The mechanism making this possible was the Vendor Interface (VINTF). VINTF created a strict, versioned API boundary between the Android Framework and the Hardware Abstraction Layers (HALs). Core OS services could now talk to the hardware using a standardized contract. If the framework wanted to turn on the camera, it just called the interface. Vendor code on the other side handled the proprietary hardware details.

Observing the architecture before and after Treble reveals a profound shift. The original monolithic design fused the OS framework directly to the vendor code. Post-Treble architecture cleanly slices them apart using an interface wall.

In the Post-Treble world, the Vendor Interface acts as a wall. A device maker could now update the Android Framework to version 9.0 while reusing the exact same pre-compiled Android 8.0 camera and GPU vendor binaries. They skipped months of driver rewrites because the interface contract remained identical.

Tip: Do not confuse Project Treble with direct updates. Treble does not mean Google pushes OS updates directly to your phone. The architecture simply makes it vastly easier and cheaper for original equipment manufacturers to build and test those updates themselves.

Treble decoupled the hardware drivers from the OS, but a major problem remained. Google was still fully reliant on OEMs to push critical security and media updates to users.

Taking Back Control (Project Mainline)

When a critical zero-day vulnerability hits a media codec, waiting even a few weeks for an OEM to build, test, and distribute a full Over-The-Air (OTA) update is unacceptable. Google needed a way to push emergency patches and framework updates directly to devices immediately. They could not keep asking the manufacturer for permission or forcing the user to install a massive firmware flash.

The platform needed an entirely new update mechanism.

Starting in Android 10, Project Mainline modularized specific internal components of the OS. Google carved out critical framework pieces and wrapped them into independent, updatable containers. The Play Store delivers these components directly to the user under the name Google Play System Updates.

The delivery format for these modules is called APEX (Android Pony EXpress). An APEX file is effectively a specialized package that can update core OS binaries securely. Think of it like updating a single tab or extension in a web browser, rather than reinstalling the entire desktop operating system.

Tracing an APEX update reveals how it bypasses the traditional firmware OTA process entirely. The payload flows straight from the Play Store to the specific module without touching the rest of the OS framework.

When a vulnerability is discovered, Google pushes a new APEX file to the Play Store. Devices download the file in the background. The user simply restarts their phone, and the system mounts the new APEX module over the old one. This completely eliminates the vendor bottleneck for critical components.

Common Mistake: Beginners often confuse standard app APK updates with APEX module updates. APK files update user-facing software. An APEX package updates low-level system daemons and native C++ libraries that live deep inside the OS framework.

This long journey from a monolithic blob to a highly modular, decoupled system is essential. It sets the absolute foundation for understanding how modern AOSP is structured today.

The evolution of Android is essentially a story of solving bottlenecks. We saw the execution pipeline shift from Dalvik JIT to ART AOT to solve performance limitations. Project Treble then sliced the OS away from the hardware to fix vendor update delays. Finally, Project Mainline modularized the framework itself to allow direct security patching.

We've established that Android is now a highly modular puzzle of decoupled components. But if the OS framework is completely isolated from the hardware drivers, how does the OS actually tell the camera to turn on? The answer lies in Android's unique Binder IPC system, which we will tear apart in the next article.