AOSP Foundations
6 min read

Dynamic Partitions

Learn how Android solved the rigid storage problem by consolidating physical partitions into a single resizable super partition.

Why OTA Updates Used to Fail on Device Storage

Imagine building a 2.6GB update for a device that only has a 2.5GB system partition. You have optimized the code completely, but the physical flash memory has hard limits. The update deployment will fail permanently. You cannot simply borrow space from another partition because the storage boundaries are fixed at the factory.

Legacy Android storage acted like a house with load-bearing concrete walls between rooms. If you wanted a bigger kitchen, you could not just push the wall into the living room. You had to tear down the entire house and rebuild it. Engineers faced this exact reality when attempting to push major upgrades. The growing size of framework services often made new system images too large for the existing factory storage layout.

Warning: Attempting to repartition a legacy device via custom scripts often led to hard-bricked devices.

To understand the storage shift, we must look at how Android previously organized flash memory. This 3-step explanation illustrates the transition from physical boundaries to a unified container. 1. The left side outlines the legacy structure where every partition has a fixed hardware limit. 2. The right side highlights the modern approach where a single container holds logical subdivisions. 3. The flow demonstrates how physical abstraction simplifies storage allocation.

The legacy layout forced manufacturers into permanent choices during assembly. Addressing this rigid layout required Google to completely change how Android views the flash chip.

The 'super' Partition and Logical Boundaries

Manufacturers needed a way to resize partitions without destroying user data. The rigid physical blocks of flash memory made dynamic resizing impossible. Android solves this constraint using the super partition. This giant physical container holds logical software-defined partitions inside it.

Think of the super partition as a large warehouse floor. You draw lines on the floor with chalk to designate storage areas for different components. If a specific area needs more space during an OTA update, you just erase the chalk line and draw it further out.

Tip: The userdata and boot partitions remain standard physical partitions completely outside of the super container.

We can inspect this structure directly to see how it works in practice. The following 3-step explanation clarifies the internal structure of the new container. 1. The outer box represents the physical flash memory chip. 2. The super partition sits inside the flash memory alongside standard physical partitions. 3. The logical partitions exist purely as software definitions mapped by the metadata block.

The metadata block tells the system exactly where each logical partition begins and ends. The physical flash chip only sees the outer super partition boundary.

adb shell lpdump

Running this command on a live device exposes the exact byte offsets of the logical partitions instead of physical boundaries. Engineers often mistakenly assume lpdump will show physical sectors, but the output proves that these boundaries are purely fluid software definitions. Now that the partitions are just software definitions in a metadata table, we need to understand how the Linux kernel reads them as real block devices.

Mapping the Magic: liblp and dm-linear

The Android operating system expects to mount standard partitions during boot. However, the system partition no longer exists on the physical storage table. The Logical Partition framework solves this by utilizing the device mapper subsystem. The device mapper intercepts read and write requests targeting logical partitions. It then translates those requests into physical byte offsets within the super partition.

Tip: Dynamic partitions are a completely separate mechanism from A/B slots.

This process happens transparently at the kernel level. This 3-step explanation demonstrates how the device mapper handles a read request. 1. The kernel attempts to read from a logical block device. 2. The device mapper intercepts the call and consults the metadata table. 3. The request translates to the correct physical offset on the super partition.

Device mapper handles all translation logic on the fly. The kernel never realizes that the logical block device is virtual.

ls -l /dev/block/mapper/

This command shows that the logical partitions appear as standard mountable block devices to the rest of the OS. A common mistake is trying to run standard disk utilities directly on these mapped devices, which risks corrupting the underlying metadata. With the kernel mapping these logical partitions perfectly while the OS is running, a new problem arises when trying to flash these partitions offline.

Why fastboot Fails: Enter fastbootd

Developers often need to flash system images manually. The traditional bootloader lacks the software drivers required to parse logical metadata or manage routing. When you execute a flash command for a logical partition in the standard bootloader, it searches the physical partition table. The table only lists the super partition. The bootloader immediately throws an error because it cannot find the target partition.

Debugging Note: Flashing logical partitions via the traditional bootloader results in a "Partition not found" fastboot error.

Android fixes this by introducing a minimal Linux environment running in userspace called fastbootd. The following 3-step explanation contrasts the two different execution environments. 1. The host PC sends a flash command via USB. 2. The traditional bootloader fails because it cannot read logical metadata. 3. The userspace daemon succeeds by parsing the metadata correctly.

Communication from the host PC remains exactly the same. The receiving environment dictates success, with userspace fastboot processing the logical translation perfectly.

adb reboot fastboot
fastboot flash vendor vendor.img

Notice the reboot command uses the fastboot argument instead of bootloader. Developers frequently make the mistake of typing adb reboot bootloader out of pure muscle memory, leading directly to partition not found errors. The flash command succeeds here because the framework properly identifies the logical boundary and resizes it if necessary.

The shift from rigid physical storage to a dynamic software-defined layout solved the OTA size crisis permanently. It fundamentally upgraded the platform storage architecture. But if partitions can now resize dynamically, how do we update them in the background without interrupting the active user session?