Battery monitoring on user phone

Jan 30, 2015

There are much more differences between mobile application development and servier-side/PC-client development. Mostly because the system resources are limitied on mobile devices. Battery is one of such resources which direclty impacts the user experiences. However, it's quite easy for a novice to write code with low efficiency and high power consumption. Even experienced programmers may produce code which occasionaly drains the battery under some circumstances. In a result, the team will soon be immersed by mass of complains from users.

Things get more complicated if the app is large-scaled, and it's difficult to reproduce the abnormal power consumption cases in test environment. So, a mechanism is required to monitor the consumption on end-user's phones --- the production environment. With the machnism, we should be able to obtain information about the circumstance, which helps to identify the hotspots.

Generally speaking the monitoring incluses three phases:

1. Consumption checking: calculate the power usage of current application periodically.

2. Behavior detection: collects the behaviors of the application which may lead to battery drain.

3. Reporting: Report the collected information of 1 and 2 to a backend server for further analysis.

Check battery consumption periodically

Implement the power consumtion calculation in a service, and then use AlarmManager to startService() repeatedly, 24 hours etc. In order to minizie the side-effect of the service, use AlarmManager.setInexactRepeating() instead of setRepeating().

On Android devices before 4.4 Kitkat, as the BATTERY_STATS permission is still applicable for 3rd party applications, thelogic of BatteryStatsHelper can be copied and applied into your application, which calculates the consumption from processes, wakelocks, WiFi traffic and sensors, not only your application, but also others installed. The Helper gets the data from an Android system service named BatteryStats. As BatteryStats is a hidden class, you need to rely on Java reflection to invoke methods and fetch field values.

On Android versions greater or equal to 4.4, you can use other methodologies to simulate the actual consumption. For example, get the CPU time from files in /proc. The data is not as accurate as from BatteryStats, but better than nothing.

With the consumpton data, a series of bars can be tested against. If any of the following bars is reached, the consumption is regarded as abnormally high. You can add any additional criteria for a broder checking.

the background power consumption of your application process is among the topest N of all the applications on the given phone.

the power consumption from wake locks and cpu is more than X% greater than last check.

(You need to decide the exact number of N and X for your business scenario)

The data from battery checking are not precise, but if you collects them from a large number of user devices, you can figure out the consumption level of current application version. Use them as a criterion verify whether the refactory of code decreases the cosumption or not.

Behavior detection

Consumption data don't tell you which part of your code eats up the battery. To get that information, use local version of XposedBridge. First identify the API which may lead to high consumption, and hook them. In your hooked method callback, collect the information as below:

AsyncTask.doInBackground(): concrete AsyncTask classes, the times and length of doInBackground() invokation.

Binder.transact(): concrete Binder classes, the times and length of the invokation. This covers the cases of inter-process Service execution.

ContentProvider.query(), .insert(), .update(), .delete(): concrete ContentProvider classes, the times and length of invokation.

SensorEventListener.onSensorChanged(), .onAccuracyChanged(): concrete classes of SensorEventListener, the times and length of invokations.

Service.start(): concrete classes of Service, the times and length of the invokations.

Thread.start(): concrete classes of Thread, the times and length of the invokations.

WakeLock.acquire(), .release(): the callers of the methods, the wakelock tags, the length of holding the wakelocks.

WifiManager.startScan(): the callers of the method, the times and length of the invokations.

WifiManager.createWifiLock(): the caller of the method, the type of wifi lock, the times of the invokations.

There should be more points beyond this list for hooking, like network operations. Also, the above only includes the points at Android SDK level. If your application relies on some 3rd party libraries, you can also hook APIs there, to have the information more accurate, because those 3rd-party APIs may be closer to the app level code.

Analysis

Collecting the consumption and behavior data is only the first step to our purpose. By analyzing a large number of this kind of data should be able to help us pick out the most suspicious points inside the code. Today I have just taken the first step, I will come back to your when I have collected more data and have some experiences of analysis stories.