Zygote
The Linux kernelintermediate

Android's kernel, and its androidisms

Android runs on Linux, but not quite the Linux you would install on a server. This page is about the four jobs the kernel does for Android, the handful of features Android bolted on, and what happened to each of them.

15 minAPI 1–37 · 17interactivequizverified 2026-10-27

Every Android phone runs a Linux kernel, and every Android phone runs a kernel that no Linux distribution would ship. That is not a criticism. A phone is a hostile environment in a way a server is not: the apps are untrusted, the memory is small, the battery is finite, and the device is expected to sleep and wake dozens of times an hour without losing a notification.

Android's answer, for its first decade, was to patch the kernel. A small set of Android-specific drivers and subsystems — the "androidisms" — handled process death, shared memory, low-memory killing, and suspend. Most of them are gone now, replaced by mechanisms the upstream kernel grew. Learning which ones survived, and why, teaches you more about Android's relationship with Linux than any block diagram.

TL;DR#

  • The kernel does four jobs Android cannot do for itself: it schedules threads on cores, manages memory, talks to hardware through drivers, and enforces security at the lowest level.
  • Android added a set of features to Linux — , ashmem, the low memory killer, wakelocks, the ION allocator, logger — and most of them have since been upstreamed, reimplemented, or deleted.
  • The pattern is consistent: an Android hack survives only until Linux grows a general way to do the same job. Ashmem became memfd, the in-kernel killer became , ION became DMA-BUF heaps.
  • The kernel is also where the "one phone, one kernel" problem lives. Devices used to fork the kernel so heavily that a security fix took years to reach users; is the fix, and it is the subject of the next page.
  • Nothing above the kernel is allowed to depend on kernel internals. Apps see system calls and device nodes, not kernel data structures — and SELinux decides which of those a process may touch.

Mental model#

Imagine a restaurant that leases space in a shared building.

The building's electrical system, plumbing and gas mains are the kernel. The restaurant does not own them, cannot rewire them, and would not want to: they are maintained by someone else who keeps them to a standard.

The restaurant's own fit-out is Android's user space: the kitchen line, the dining room, the menu. It is where all the visible work happens.

Now the interesting part. Over the years the tenant added a few dedicated circuits of its own: a special high-current line for the espresso machine, a bypass for the freezer, a breaker it could throw itself. Those are the androidisms. They worked, they were specific to this restaurant, and the landlord disliked every one of them.

Eventually the building installed standard high-current outlets and a proper emergency circuit. The tenant removed its custom wiring, one piece at a time — not because the wiring was bad, but because the standard version was maintained by someone else, would survive the next renovation, and came with an inspector.

Where the analogy breaks#

The kernel is not a passive utility waiting to be used. It actively decides which thread runs next, which page gets evicted, and which process dies. Those decisions are Android's behaviour as much as the framework is: when your app is killed in the background, the decision was made by a Linux mechanism, informed by a number the framework computed.

The analogy also breaks on maintenance. Rewiring a restaurant is a one-time cost. Carrying a kernel patch is a permanent one: every time the kernel rebases onto a new long-term-support release, every out-of-tree patch has to be rebased, re-tested and re-signed. That recurring cost is the real reason the androidisms disappeared.

The big picture#

Loading diagram…
Text description of this diagram

A timeline from 2008 to 2022 showing what happened to the features Android added to the Linux kernel. On the upstreamed-or-reworked lane: binder arrives in the kernel staging tree in 2008, wakelocks are proposed to the Linux kernel mailing list in 2009 and rejected, a mainline userspace wakeup-source interface appears in 2012, and binder leaves staging in 2018. On the moved-to-userspace lane: the in-kernel low memory killer is removed upstream in kernel 4.12 in 2017 and replaced by the lmkd daemon. On the replaced-by-a-standard lane: ION is switched off in the android12-5.10 branch in 2021 in favour of DMA-BUF heaps, and ashmem is deleted from the kernel staging tree in 2022 because memfd replaces it.

Read that diagram as a scoreboard. Three outcomes are possible for any Android-specific kernel feature, and each one tells you something different:

OutcomeWhat it meansExamples
UpstreamedThe idea was good enough that Linux adopted it, sometimes in a different formBinder, wakeup sources
Moved to user spaceThe kernel was the wrong place for the policyLow memory killer → lmkd
Replaced by a standardLinux grew a general mechanism that covered the special caseAshmem → memfd, ION → DMA-BUF heaps

Deep dive#

The four jobs#

Strip away the branding and the kernel does four things for Android.

It schedules. Every thread in every process is a Linux task, placed on a core by the completely fair scheduler or one of its energy-aware variants. Android's contribution here is mostly tuning: init.rc writes scheduler parameters at boot, and the framework communicates process importance to the kernel through scores so the scheduler and the killer can make consistent choices.

It manages memory. The kernel owns the page tables, the page cache, anonymous memory and the sharing that makes Zygote's fork cheap. It also owns the pressure signals — — that tell user space the device is running out of room, and the cgroup hierarchy Android uses to group processes for accounting and throttling.12 Android has no swap partition by default, so memory pressure is resolved by killing processes rather than by paging. That single design choice shapes the whole memory story.

It drives the hardware. Cameras, displays, modems, touch controllers, sensors: all reached through kernel drivers, usually through a vendor-supplied module. The kernel is the only code that can touch a register.3

It enforces. Every process has a . Every file, socket and device node has an owner, a group and a mode. On top of that, applies a mandatory policy that is checked on every access, so even a process running as root is confined to a domain and can only do what the policy allows.4

Everything else in this module is a consequence of one of those four.

The androidisms, and what happened to them#

Binder is the survivor. Android needed an IPC mechanism that could pass file descriptors, identify the caller's UID reliably, and manage one-way calls without a thread per client. It built one as a character device, and the driver eventually moved out of the kernel's staging tree into the main kernel proper. It is now a normal Linux subsystem: other Linux users can enable it, and exists so containers can mount a private set of Binder devices instead of sharing /dev/binder.5

Ashmem was Android's shared-memory driver, a way for two processes to map the same pages. It was replaced by memfd_create, which has existed in Linux since 3.17 and gives you an anonymous file descriptor you can map, seal and pass over a socket. The kernel commit that deleted ashmem said it plainly: the mainline replacement is memfd. Android's own libraries had already grown a compatibility layer that picks whichever is available.6

The low memory killer started life as an in-kernel driver that watched free memory and killed processes below hard-coded thresholds. It was removed from the upstream kernel in 4.12, and Android moved the policy to a user-space daemon, , which reads PSI pressure and memory cgroup state and kills the least important process. The move is a small masterpiece of design correction: the kernel now reports pressure, and user space — which actually knows what a process is for — decides who dies.7 See Process importance and the OOM killer for the framework side of that number.

Wakelocks are the most interesting case, and they get their own page. The short version: Android's implementation was rejected upstream, Linux grew wakeup sources instead, and modern Android routes its suspend decisions through a system service rather than writing to a driver. The idea — a component declares that it needs the device awake — survived. The API did not.

ION allocated graphics and multimedia buffers. It was replaced in Android 12 by DMA-BUF heaps, which expose one character device per heap under /dev/dma_heap/, so SELinux can allow a process to use the cached system heap but not the uncached one, or a camera heap but not a display heap. That per-heap control was impossible when every allocation went through /dev/ion.8

Two more are worth knowing by name because you will see them in kernel logs and old documentation: logger, the original kernel log buffer that logd replaced, and ashmem's pinning API, which had no equivalent until memfd grew seals. Neither is something you should write new code against.

is the newest addition, and it is a different shape entirely: rather than a new driver, it is a general kernel facility that Android uses for per-UID network accounting and for collecting power and memory statistics without a kernel patch.9

Why Android needed them at all#

It is tempting to read the list above as a series of mistakes. It was not. Each feature solved a problem that mainstream Linux in 2008 had no answer for.

A server can swap. A phone cannot, or should not: flash storage wears out, and swapping to it makes the device feel broken. So Android needed a killer, not a pager.

A server's processes are trusted and long-lived. A phone's apps are neither, and the user expects the home screen to appear instantly. So Android needed a cheap fork with preloaded classes, and it needed to kill frozen apps without waiting for the OOM killer to fire.

A server never suspends. A phone suspends constantly, and a download in progress must not be interrupted. So Android needed a way for user space to say "stay awake".

The androidisms were correct answers to real constraints. What changed is that the constraints became general — every Linux user has a battery now — and the kernel grew standard answers.

The boundary: what the kernel exposes#

Everything above the kernel talks to it through four narrow interfaces, and knowing which one a behaviour uses tells you a lot about how portable it is.

InterfaceWhat it isExample
System callsThe C ABI of the kernelfork, mmap, ioctl
Device nodesCharacter and block devices under /dev/dev/binder, /dev/dma_heap/system
Pseudo-filesystemsprocfs, sysfs, bpf, tracefs/proc/pressure/memory, /sys/power/state
Netlink and socketsKernel-to-user event streamsuevents from the driver core

Two things follow. First, the kernel has no idea what an app is. It sees UIDs, tasks, file descriptors and pages. Every concept Android cares about — activity, service, package — is reconstructed above the boundary. Second, because the boundary is narrow, it can be policed. SELinux labels apply to device nodes and files; filters the system calls a process may make at all; both are enforced by the kernel because user space cannot be trusted to enforce anything.10

The kernel is where the update problem lives#

One more thing belongs in an overview, because it dominates the next page. A kernel is compiled for a specific board: it knows the memory map, the interrupt controller, the camera's power sequence. For most of Android's life, every phone shipped a kernel that its SoC vendor had forked, patched and never rebased. A security fix in Linux might take years to reach a device, if it arrived at all.

The was the first attempt at a shared base: Linux LTS plus Android's patches.11 It was not enough, because vendors still changed the kernel itself. GKI changed that by making the kernel a fixed binary and moving board support into loadable modules behind a stable interface, the . From Android 12, devices shipping with kernel 5.10 or higher must ship the GKI kernel.12 That story continues in Branches, GKI and the KMI.

How it evolved#

Kernel versions per Android release are a good proxy for how entangled the two used to be. Android 1.0 shipped on 2.6.25; Android 10 spanned 4.9 and 4.14; Android 16 ships on 6.12.

Two patterns stand out. The first is that the platform used to track the newest kernel it could, then settled into long-term-support releases: 3.18 for Marshmallow, 4.4 for Oreo, 4.19 and 5.4 for Android 11. The second is that a single Android release can appear on two kernel versions. Android 10 shipped on both 4.9 and 4.14; Android 14 on both 5.15 and 6.1. To a developer this is invisible. To a vendor it is the entire problem, because it means "Android 14" does not identify a kernel ABI.

Under the hood#

The places to start reading, if you want to see the boundary for yourself:

Curiosity corner#

  • Android's kernel has no GNU in it. No GNU coreutils, no glibc, a different init, a different shell. The kernel is Linux; everything above it is Android's own user space. That is why ps flags you know from a server often do not work.
  • The most important kernel feature for app performance is a fork. fork() with copy-on-write pages is what lets Zygote hand a new app a preloaded heap without copying it.
  • Binder is now a general Linux feature. It is not an Android-only driver any more, and binderfs exists specifically so non-Android users can have their own instances.

Try it#

Kernel identity and pressure

adb shell uname -a adb shell getprop ro.kernel.version adb shell cat /proc/pressure/memory

Example output
Linux localhost 6.6.30-android15-8-g8f1d2c0a1b2c #1 SMP PREEMPT ... aarch64 Toybox
ro.kernel.version=6.6
some avg10=0.42 avg60=0.31 avg300=0.18 total=4823911
full avg10=0.00 avg60=0.00 avg300=0.00 total=0

If /proc/pressure/memory does not exist, the kernel was built without CONFIG_PSI, and lmkd has to fall back to the older vmpressure events. That single missing file changes how the device behaves under memory pressure.

Build it#

The N06 lab walks through building an out-of-tree module against a GKI kernel. It is the most direct way to feel why the KMI exists: the module either matches the kernel's symbol versions or it refuses to load.

Check yourself#

Check yourself

1 / 12

Ashmem, Android's original shared-memory driver, was replaced by which standard Linux mechanism?

0 answered · 0 correct

Sources#

Sources

Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.

  1. docsPressure Stall Information (PSI)docs.kernel.orgaccessed 2026-10-27↩
  2. docsControl Group v2docs.kernel.orgaccessed 2026-10-27↩
  3. docsAndroid kernel overviewsource.android.comaccessed 2026-10-27↩
  4. docsSELinuxsource.android.comaccessed 2026-10-27↩
  5. docsbinderfs — Linux kernel documentationdocs.kernel.orgaccessed 2026-10-27↩
  6. docsmemfd_create(2) — Linux man pagedocs.kernel.orgaccessed 2026-10-27↩
  7. docsLow-memory killer daemon (lmkd)source.android.comaccessed 2026-10-27↩
  8. docsDMA-BUF heapssource.android.comaccessed 2026-10-27↩
  9. docseBPF on Androidsource.android.comaccessed 2026-10-27↩
  10. docsSeccomp BPFdocs.kernel.orgaccessed 2026-10-27↩
  11. docsAndroid Common Kernelsource.android.comaccessed 2026-10-27↩
  12. docsGeneric Kernel Image (GKI)source.android.comaccessed 2026-10-27↩