AOSP Framework & Internals
7 min read

Power HAL & USB HAL

Learn about Power HAL & USB HAL.

Bridging the Hardware Divide

Android runs on hundreds of different processor architectures. The framework cannot natively understand the exact frequency scaling curves of a custom chip or the specific USB controller logic of a proprietary board. Hardcoding these details into the operating system would make updates impossible and hardware integration chaotic.

Operating systems require a standardized way to issue commands without knowing how the hardware executes them. The Power HAL and USB HAL solve this problem for two critical control planes. While the Power HAL dictates CPU frequencies and thermal constraints, the USB HAL manages port behaviors and data roles.

These interfaces separate the intent from the implementation. The framework decides when to boost performance or switch USB modes, while the vendor layer handles the actual hardware registers. This abstraction keeps the core system flexible.

So how does the framework actually talk to the hardware when a user interacts with the device? The answer starts with the CPU.

Bypassing Slow CPU Scaling

CPU frequency scaling takes time to happen organically. When a user swipes a list, the processor load naturally increases, and the kernel eventually responds by ramping up the clock speed. Waiting for this organic kernel response causes the first few frames of the animation to drop. The framework knows the user intent before the processor feels the load.

The IPower HAL gives the operating system a direct line to bypass the sluggish reaction time of the kernel. System services use Power Hints to force immediate hardware changes. Whenever a touch event occurs, components like SurfaceFlinger immediately fire an INTERACTION hint down the stack.

Vendor implementations catch this hint and force the CPU to a higher frequency instantly. This proactive scaling guarantees smooth scrolling from the very first frame. Games require a completely different approach using a SUSTAINED_PERFORMANCE flag. That signal tells the hardware to intentionally cap peak frequencies, preventing severe thermal throttling during long gaming sessions.

The framework uses the sendHint method to pass the intent. This function translates the abstract UI event into an actionable hardware request.

Return<void> sendHint(PowerHint hint, int32_t data);

Common Mistake: Developers sometimes assume the framework edits /sys/devices/system/cpu/ files directly. Android never touches sysfs directly for power management, because it always delegates to the Power HAL.

You are about to see a sequence diagram showing how the framework signals the hardware layer to adjust CPU frequency before a frame even renders. This helps visualize the immediate reaction triggered by user input. Pay attention to how SurfaceFlinger does not wait for a response before continuing its rendering pipeline.

HAL layers translate the high-level intent into the precise system writes required for that specific chipset. But hitting the maximum frequency for every touch is not always the best strategy.

Hitting Frame Deadlines with PowerAdvisor

Sending a broad interaction hint is a blunt instrument. Modern displays run at 120Hz, leaving only 8.3 milliseconds to render each frame. Ramping the CPU to maximum speed for every touch event wastes massive amounts of battery. Running the processor too slow causes dropped frames.

SurfaceFlinger contains a framework component called PowerAdvisor to solve this exact timing issue. This subsystem analyzes the rendering pipeline and predicts the exact amount of CPU time required for the upcoming display frame. It communicates this prediction directly to the Power HAL.

Vendor code then dynamically scales the CPU frequency to hit the 8.3ms deadline precisely. Granular control minimizes wasted clock cycles while ensuring perfect frame pacing. The system balances performance and efficiency perfectly.

Power management happens quietly in the background. USB interactions, however, require constant user input and role switching.

Reconfiguring the USB Port

A modern Type-C port is not a single-purpose plug. It acts as a battery charger, a media storage drive, a MIDI keyboard, or a debug bridge depending on the situation. Android needs a way to switch these hardware roles dynamically based on what the user taps in the notification shade.

The IUsbGadget HAL handles this role switching entirely. Selecting File Transfer from the UI causes the system to instruct the HAL to unbind the current USB functions. Vendor implementations then bind the required kernel driver directly to the USB controller.

This interface defines how the framework requests a function change. The method accepts a bitmask of desired functions and a callback to notify the system when the switch completes. It returns no direct output, relying entirely on the asynchronous callback for success confirmation.

interface IUsbGadget {
    void setCurrentUsbFunctions(in long functions, in IUsbGadgetCallback callback, in long timeout);
}

Tip: Always pass a valid timeout value to setCurrentUsbFunctions. Kernel drivers can occasionally hang during function switching, and the framework needs a way to recover if the hardware stops responding.

HAL implementations take the generic bitmask and apply the vendor-specific driver configurations needed to transform the identity of the port. But switching roles assumes the system already knows a cable is plugged in.

Detecting Cable Connections

Type-C ports are complex mechanisms that must negotiate power contracts and determine if the device is a host or a peripheral. Framework services cannot poll the port continuously without draining the battery. Continuous polling would keep the processor awake indefinitely.

The IUsb HAL provides an event-driven mechanism to alert the system only when necessary. Physical cable insertion causes the kernel driver to detect a change in the port resistor values. A vendor USB daemon running in the background monitors these hardware interrupts.

This background process reads the new state from the kernel and fires an asynchronous callback through the HAL. Callbacks eventually reach UsbPortManager in the framework. The framework then triggers the appropriate charging logic and displays the USB options UI.

You are about to see a flowchart mapping how a physical cable insertion travels up the stack. This helps clarify the strict separation between hardware detection and the framework response. Notice how the HAL acts as the messenger crossing the boundary between vendor space and the core operating system.

Operating systems never need to understand the electrical nuances of the Type-C specification. They only listen for clean HAL events.

Tying It Together

The Power HAL and USB HAL demonstrate exactly why the Android architecture separates intent from implementation. High-level code decides when to boost performance or switch USB modes, but it trusts the vendor layer to execute those decisions safely. This abstraction keeps the OS flexible enough to run on drastically different hardware platforms.

You now understand how Android manipulates hardware states without touching driver code. The system triggers state changes through defined interfaces. The vendor code handles the messy hardware details.

The next logical step involves exploring how the system protects these critical hardware paths. We will look at how SELinux restricts access so that only trusted services can invoke these HAL commands.