When it comes to mobility, battery life is a paramount concern and a key selling feature for mobile phone manufacturers. Tech giants such as Google and Apple spend a significant amount of time to fully optimize and improve battery life and performance characteristics of their mobile devices. Recent concerns about iOS11’s hunger for power consumption indicates a 60% higher battery decay rate causing many to hold off updating to the latest iOS version which could potentially cause significant business and brand damage to a Apple. While batteries are getting better, demand for power is also increasing with the proliferation and ubiquity of services available on smartphones.

Below we try to get into the details of power and battery management in Android’s open source code. When it comes to mobile computing, Battery management is a unique challenge. On one hand it’s a critical component of any service that runs on a device (as opposed to telephony, gps and camera services that are only used in a more limited setting). Yet at the same time, battery management involves direct interaction with a hardware device (battery). In addition, interacting with a hardware device means one needs to worry about dependencies to various hardware manufacturers. Therefore, an intricate software architecture mechanism is needed to provide various levels of abstraction to satisfy above requirements.

Android’s software architecture addresses these design concerns via an intricate and fairly complex layered architecture. The high level Android Application Framework provides developers a rich set of functionalities to interface and interact with the device and its various services and components. These services are wrapped in intuitive APIs mostly managed by the system_server process. Application framework communicates with Native modules via intricate IPC proxies to enable an elegant abstraction layer while accessing a set of Android’s system services such as display, connectivity, camera, battery and power. These services implement a hardware abstraction layer (HAL) for various hardware components of the device such as battery. The chart in next page depicts a rough high level class/interface hierarchy between various C++ and Java classes for battery and power management (this chart is by no means a comprehensive graph!).

Underneath these services the Linux kernel provides device drivers with lowest levels of functionalities in additions to Linux’s core offering such as memory management or power management. Android needs to operate with very limited energy footprint. Therefore, it implements a power management driver on top of standard Linux power management.

As the chart above suggests at the top of the hierarchy sits the PowerManagerService class which imports various functionalities from other high level Java classes (such as BatteryStatsService, BatterySaverPolicy, BatteryManager, etc.) as well as employing more low-level C++ methods defined in PowerManagerService.cpp.

The PowerManagerService class is responsible for coordinating power management functions of the Android Device. Upon startup, PowerManagerService loads an instance of Power HAL which calls an init() function. The init() function performs power management setup actions at runtime startup such as setting default cpu freq parameters. There is also a setInteractive() function that performs power management actions for various system’s states (i.e., interactive, non-interactive interfacing with kernel for low level power interfaces.)

The PowerManagerService class uses various methods provided in BatteryStatsService.Java which uses JNI to get low level stats natively implemented in C++ (BatteryStatsService.cpp) such as getPlatformLowPowerStats(). It also uses native calls to PowerManagerService.cpp such as nativeSetInteractive(), nativeSendPowerHint() and nativeSetFeature() functions. The BatteryStatsService class is still fairly high level and provides a nice layer of abstraction for various functionalities implemented in other Java classes such as BatteryStatsImpl and BatteryStats.

 As discussed above, the PowerManagerService through the use of BatteryStatsService class utilizes BatteryStats class to gain access to battery usage statistics, including information on wakelocks, processes, packages, and services. The BatteryStats class also includes background timers and counters for sensor, bluetooth, Wifi and other hardware components to calculate the power use of a device subsystem or an app.

Before we move forward, we need to discuss wakelocks as they are an important component of Android’s power management architecture.

Wakelocks

Battery power is an extremely precious and limited resource. Therefore, Android OS forces the device to quickly fall asleep if left idle. There is however the need for an application to wake up the screen, gain access to CPU and keep the screen on while it finishes a process. A wake lock is a mechanism to indicate that an application needs to have the device stay on. Wakelocks allow Android to implement a more aggressive power management policy (due to battery constraints) compared to standard Linux. To keep the CPU running to complete the work, apps use the Wakelocks system service provided by the PowerManager. PowerManager is included in the PowerManagerService class to provide access to wake locks.

The PowerManager constructor sets context, service and handler for power manager and provides a set of get interfaces regarding brightness levels, reasons for reboot, etc. Applications such Bluetooth, Calendar, Camera, Alarm clock, etc. use power manager to push a certain device state (for instance when an alarm goes off or a calendar alert is received). However, its primary API is newWakeLock() which will create a PowerManager.WakeLock object (with various lock levels). You can then use methods on the wake lock object to control the power state of the device. The acquisition of a wakelock through the acquire() method forces the screen to stay on until the release() function is called. Wakelocks provide an interface for notifying power manager of a user activity such as a touch event or key is pressed. The WakeLock object’s methods allow API users to control the power state of the device. The PowerManager class however allows certain application packages to ignore battery optimization through being whitelisted.

Lastly, the PowerManager class interfaces with kernel through native calls in IPowerManager.cpp for things like rebooting the device. The IPowerManager is part of Android’s native framework. It implements acquireWakeLock() and releaseWakeLock() methods to access underlying hardware (such as battery in this case). The standard interface is defined by a Hardware Abstraction Layer (HAL) file to create separation of concern and decouple underlying hardware implementations with higher level implementations.

The BatteryStatsImpl class extends BatteryStats. It provides various low level stats that other processes need. For instance The Class SystemClocks provides total time in milliseconds spent executing in Kernel or in user code or keeping track of battery levels during the last plug and unplug event. It also includes methods such as updateMobileRadioState() that distribute cell radio (or Bluetooth) energy information and network traffic to apps requesting them. It uses a set of helper functions in BatteryStatsHelper to retrieve power usage information for various applications and services. It also provides low level memory stats from the kernel and includes methods such as updateCpuTimeLocked() which include the logic for reading and distributing CPU usage across apps. For instance it decides to give more of CPU time to those apps holding partial wakelocks if the screen is off and we are on battery. It also uses methods like writeSummaryToParcel()[1] to provide summary statistics to be written to disk with various stats about battery and CPU usage.

The BatteryStatsImpl class uses a set of methods and attributes defined in BatteryManager class to query battery and charging properties (e.g., battery temperature, whether it’s plugged, current battery’s maximum charging voltage). The BatteryManager class also provides strings and constants used for the ACTION_BATTERY_CHANGED. These methods (such as isCharging()) are used by other services to performs things that user does not care about and need to only be performed when the battery is at full charge. It uses Native methods from BatteryService.cpp to accomplish some of the above tasks. BatteryManager also broadcasts an action to notify other services of the charging state of the battery (i.e., whether phone is plugged or not). It also uses a bp file (a modern/simpler alternative to NDK) to specify certain C++ files (such as BatteryProperties.cpp, BatteryProperty.cpp, etc.) to be included during the build.

The BatteryService.cpp class creates a static Singleton Instance of BatteryService to provide low level battery services. It also supports functions such as adding and removing sensors. These methods are used by SensorService.cpp which manages registration of sensors such as Gyroscope.

 Other Battery and Power Management Components

While the list below is not comprehensive. It includes some of the other key classes and interfaces that are used/referenced by the above classes.

BatterySaverPolicy.java

A class to determine if the battery saver mode needs to be turned on for a specific service. It includes a set of attributes to track whether things like sound triggers, animation, full backup, vibration etc. are disabled during battery saver mode. Also provides a get method to retrieve state data containing battery saver policies.

 BatteryProperty.Java

This class provides a get method for retrieving battery properties that may be queried using BatteryManager.getProperty(). It sends calls to native implementations in BatteryProperty.cpp

 BatteryProperties.java

A set of Parcel read/writes in sync w BatteryProperties.cpp for attributes such as charge/battery status/temperature/voltage etc. The two methods readFromParcel() and writeToParcel() are used to communicate with BatteryProperties.cpp

 PowerManagerInternal.java

This class is only used by the system server. It provides attributes like WAKEFULNESS_ASLEEP, WAKEFULNESS_AWAKE and WAKEFULNESS_DREAMING which maintain the device state.

IBatteryStats.aidl

This Android Interface Definition Language file defines the interface that client and service agree to while communicating via Interprocess Communication (IPC). These Java-like interfaces include things related to Battery Stats such as noteStartVideo(), noteStopVideo() (and audio equivalents), noteStart/StopCamerca(), etc.

The IBatteryStats.cpp file defines a set of virtual functions such as Start and Stop of Video and Audio or Start and Stop of other sensors. Each using the writeIntefaceToken() method for efficient IPC transport.

IPower.hal (Hardware Abstraction Layer)

Given the hardware dependency of several Android components, a Hardware Abstraction Layer allows porting Android to an OEM-specific hardware where each vendor writes its own drivers. Constructor for this interface performs power management setup actions at runtime startup, such as to set default cpufreq parameters. The power.h header file allows multiple OEM-specific power headers to be defined.

Power.cpp

Implements methods defined in Ipower HAL and is used by PowerManagerService.

BatteryServiceConstants.h

Contains enums representing various battery status (e.g., charging, discharging, full) and health (e.g., good, overheat, dead, cold) states.

BatterySipper.java

Contains power usage of an application, system service, or hardware type. For instance wifi, gps, other sensors, camera, flashlight, Bluetooth, etc.

DreamManagerService.java

The DreamManagerService class provides Service APIs for managing dreams. Dreams are interactive screensavers launched when a charging device is idle, or docked in a desk dock. Dreams provide another modality for apps to express themselves, tailored for an exhibition/lean-back experience.

As we discussed in the beginning of this post, Android’s limited available power resources and the high demand for power usage across various native and user-defined Android apps requires an elegant yet complex software architecture to allow versatility and performance for the platform. While this is not meant to be a comprehensive and in-depth overview of Android’s power and battery management components and services, we hope it sheds light into a fairly complex yet critical part of the Android Framework.

[1] Parcels are containers for data and object reference messages used for high performance IPC communication.