androidengineers.Book a session

Android Architecture Fundamentals

Android OS Architecture (Kernel → HAL → Framework → Apps)

article30 minMedium

Overview

Android is a layered software stack. Each layer depends only on the layer below it, which lets hardware vendors and OEMs customize lower layers without breaking app compatibility. From bottom to top: Linux Kernel → HAL → Native Libraries + ART → Java/Kotlin Framework → Applications.

Understanding this stack matters when you optimize performance, debug native crashes, or reason about IPC costs and security boundaries.


Layer 1: Linux Kernel

Android runs on a modified Linux kernel (currently based on LTS kernels such as 5.10 / 6.1). Key customizations:

  • Binder IPC driver/dev/binder. The single most important Android-specific addition. All system service calls pass through it.
  • Ashmem / memfd — anonymous shared memory for zero-copy data sharing.
  • Low Memory Killer (LMK) — an in-kernel driver that kills processes based on oom_adj score when free memory is low (now moved to userspace LMKD on modern kernels).
  • Wakelocks — prevent CPU or display from sleeping during critical operations.
  • ION memory allocator — contiguous memory allocation for GPU and camera.

The kernel provides process isolation: every Android app runs in its own Linux process with a unique UID, giving security at the OS level.


Layer 2: Hardware Abstraction Layer (HAL)

HAL lives between the kernel drivers and the Android framework. It exposes stable C/C++ interfaces so the framework can work with hardware without knowing driver specifics.

Android 8 (Treble project) introduced HIDL HALs (run in separate processes), and Android 11+ introduced AIDL HALs. This separation means:

  • HAL processes (vendor partition) can be updated independently of the framework (system partition).
  • A camera HAL crash does not crash the system server.

Common HALs: Camera, Audio, Bluetooth, GPS, Sensors, Graphics (Gralloc, EGL), Fingerprint.

System Server (Java) ──Binder──► Camera HAL process ──ioctl──► kernel driver

Layer 3: Native Libraries & ART Runtime

Bionic libc

Android's C library. Smaller than glibc, Apache-licensed. Provides malloc, pthreads, math. No execinfo.h; stack unwinding uses libunwind.

Key native libraries

LibraryPurpose
OpenGL ES / VulkanGPU rendering
SQLiteEmbedded relational database
WebKit / ChromiumWebView rendering
OpenSSL / ConscryptTLS
Skia2D graphics used by Canvas
libc++C++ standard library

Android Runtime (ART)

ART is the managed runtime for Java/Kotlin code. It replaced Dalvik in Android 5.

  • AOT compilation (dex2oat) — .dex bytecode → native .oat file at install time (or device idle time on Android 7+).
  • JIT + Profile-Guided Optimization (PGO) — hot methods identified at runtime, background-compiled to native code. See the ART vs Dalvik article for depth.
  • GC — concurrent, generational. GC pauses are short but not zero; see the Memory Management article.

ART runs in the app's process. Each app has its own ART instance forked from Zygote.


Layer 4: Java / Kotlin Framework

This is what most Android developers interact with. The framework is implemented as Java/Kotlin classes compiled to DEX, running on ART in the System Server process and in each app process.

Key framework services (all running in System Server)

ServiceResponsibility
ActivityManagerService (AMS)Activity/task lifecycle, process management
WindowManagerService (WMS)Window layout, input dispatch, rotation
PackageManagerService (PMS)APK install/query, intent resolution
InputManagerServiceTouch, key events
PowerManagerServiceWakelocks, screen on/off
ConnectivityServiceNetwork state
NotificationManagerServiceNotifications, channels

Apps talk to these services exclusively through Binder. The Java stub you call (e.g., getSystemService(Context.ACTIVITY_SERVICE)) marshals arguments into a Parcel, sends it over Binder to the server process, and blocks waiting for the reply.

Context

Context is the gateway to framework services and resources. Application context lives as long as the process; Activity context ties to the Activity lifecycle. Using Activity context for long-lived objects (singletons, static fields) is the classic memory leak.


Layer 5: Application Layer

Apps ship as APKs (zip archives containing classes.dex, resources, AndroidManifest.xml, native .so files). The package manager installs them under /data/app/.

Each app is assigned a unique Linux UID (app_NNNN). The app's data directory (/data/data/com.example.app/) is owned by that UID.


How an App Launch Crosses All Layers

User taps icon
   │
   ▼
Launcher calls startActivity(intent)
   │  (Binder call to AMS)
   ▼
ActivityManagerService (System Server)
   │  decides process doesn't exist
   │  sends socket message to Zygote
   ▼
Zygote forks new process
   │  (Linux fork() + CoW pages)
   ▼
New process: ActivityThread.main()
   │  attaches to AMS via Binder
   ▼
AMS sends bindApplication() transaction
   │
   ▼
App: Application.onCreate()
   │
   ▼
AMS sends launchActivity() transaction
   │
   ▼
App: Activity.onCreate() → onStart() → onResume()
   │
   ▼
SurfaceFlinger composites first frame
   (crosses into kernel via Binder/ioctl to GPU driver)

Every arrow that crosses a process boundary is a Binder transaction with serialization cost.


IPC Overview

Android uses Binder for nearly all IPC. Key properties:

  • 1 MB transaction limit per process pair (actually 1 MB – 8 bytes shared across all threads). Sending large Bitmaps over Binder throws TransactionTooLargeException.
  • Synchronous by default — calling thread blocks until the remote returns.
  • Thread pool — each process has a Binder thread pool (default 15 + 1 threads) to serve incoming calls.
  • Security — kernel stamps each transaction with the calling UID/PID; the server can call Binder.getCallingUid() to check permissions.

For large data, use SharedMemory (ashmem) or FileDescriptor passed over Binder instead of raw bytes.


Practical Gotchas

  • Never do Binder calls on the main thread in release code. They can block if the server is busy, causing ANRs. Enable StrictMode.ThreadPolicy to catch this in debug builds.
  • Context leaks — storing Activity context in a static field or singleton keeps the entire Activity (and its View tree) alive after it is destroyed.
  • Native library ABI — if your app ships .so files, list all supported ABIs in build.gradle (abiFilters). A mismatched ABI causes UnsatisfiedLinkError at runtime.
  • HAL version mismatch — on older devices, a HAL your code depends on may not be present or may behave differently. Always check PackageManager.hasSystemFeature() before using hardware features.
  • System Server is single-process — a crash there reboots the phone. Avoid contributing to it; keep AIDL interfaces minimal and robust.

Summary

LayerProcessLanguageExamples
Kernelkernel spaceCBinder driver, LMK, wakelocks
HALvendor processesC/C++Camera HAL, Audio HAL
Native librariesapp processC/C++Bionic, Skia, SQLite
ARTapp processGC, AOT/JIT, DEX execution
FrameworkSystem Server + appJava/KotlinAMS, WMS, PMS
Applicationapp processJava/KotlinYour code

Knowing which layer a problem lives in — a GC pause vs. a Binder delay vs. a HAL bug — determines where to look in the profiler and how to fix it.

Open learning resource ↗

YOUR LEARNING JOURNEY

0 of 177 available lessons completed

Progress saved in this browser. No account needed.
Android OS Architecture (Kernel → HAL → Framework → Apps) | Android System Design | Android Engineers