The Six Layers That Turn an Android View.draw Call Into Screen Pixels
Android graphics debugging is notoriously opaque because the stack is deep and the terminology dense. Knowing which layer owns a given problem — a dropped frame, a screenshot that comes back black, a SurfaceView tearing artifact — cuts diagnosis time from hours to minutes.
Every frame on an Android screen travels through six distinct layers: App UI, Drawing Interface, Rendering Engine, Buffer Channel, Composition Coordination, and Hardware Display. The top three layers live on the application side and turn View trees into pixel data; the bottom three live on the system and hardware side and handle buffer management, multi-window composition, and physical output. Two parallel threads run through the stack — a data flow that moves pixels from Canvas to Display, and a control flow where WindowManager configures layers and SurfaceFlinger composites them. The whole system is glued together by the Producer/Consumer model: rendering engines, Camera, and video decoders act as producers writing into BufferQueues, while SurfaceFlinger, ImageReader, and encoders act as consumers. Gralloc-allocated buffers pass between GPU, HWC, and CPU with zero copy, and Fence primitives keep asynchronous hardware operations correctly ordered. Different graphics scenarios — a game, a camera preview, a DRM-protected video — differ only in which layer they plug into and which consumer sits at the other end of the BufferQueue.
The six-layer model reveals that Canvas sits at an awkward boundary — it is both the app's drawing API and the rendering engine's input format, which explains why Canvas behavior differs subtly between software and hardware rendering paths.
BufferQueue is the single most load-bearing abstraction in the system. Nearly every graphics scenario — game rendering, camera preview, video playback, screen recording — is just a different producer-consumer pair connected through a BufferQueue.
SurfaceFlinger's preference for HWC over GPU composition is a power-saving strategy baked into the architecture, not an optimization afterthought. The system will burn GPU cycles only when the display controller hardware literally cannot handle the layer count or blend mode.
The separation of WindowManager (policy) and SurfaceFlinger (pixels) into different processes is a real-time constraint decision: composition cannot afford to be blocked by window lifecycle logic, so it runs in its own process with SurfaceControl as the narrow bridge.