Android Battery Mocking tutorial

Kaushik N. Sanji
7 min readMay 14, 2018

--

This article does not require the user to do a real world autopsy on the Android Device to remove the Battery for doing some testing. As the title states “Mocking”, this only describes how to test for the Battery charging state and other related services. This can be tested on the real device but certain care must be taken to roll-back every changes committed on the real device. Hence I do not recommend you to do this on your daily rider. If you can spare a device then you can go ahead with it or else test on the Android Virtual Device. This article describes all testing done using the Android Virtual Device, and the steps are applicable to real devices in almost the same way.

Let’s begin mocking with ADB Shell

  • Android system provides many system services for its proper functioning.
  • The system services can be checked/inspected through the adb shell dumpsys <service> command. dumpsys is a tool that runs on Android devices and provides all the information about system services.
  • To see the list of system services on your device, use the command adb shell dumpsys -l.
  • Among the list of system services, battery is a system service that gives information related to the device’s battery.
  • To get the help on this batteryservice, run the command adb shell dumpsys battery –h. Output of this will be something like below -
Battery service (battery) commands:help    Print this help text.set [ac|usb|wireless|status|level|invalid] <value>    Force a battery property value, freezing battery state.unplug    Force battery unplugged, freezing battery state.reset    Unfreeze battery state, returning to current hardware values.
  • To know the current service state of the battery service, run the command adb shell dumpsys battery. Output of this will be something like below -
Current Battery Service state:
AC powered: true
USB powered: false
Wireless powered: false
Max charging current: 0
Max charging voltage: 0
Charge counter: 0
status: 2
health: 2
present: true
level: 100
scale: 100
voltage: 0
temperature: 0
technology: Li-ion
  • AC powered/USB powered/Wireless powered” shows the states of power source connected to the device.
  • status” shows the battery status.
  • level” represents the battery charge value. The Scale of this is defined by the “scale” property. So the range of “level” property is from 0 to “scale” value.
  • health” represents the battery health.
  • voltage” tells the voltage value. To retrieve the actual value, one needs to divide the value shown by 1000.
  • temperature” tells the battery temperature. To retrieve the actual value in Celsius, one needs to divide the value shown by 10.

For more information, please refer the BatteryManager source documentation.

To Simulate Power source Connected/Disconnected on Emulator using ADB

  • Execute the command adb shell dumpsys battery to see the current status of the battery properties.
  • If any of these “AC powered/USB powered/Wireless powered” shows ‘true’, it means the corresponding power source is connected. To begin with our tests, we will first make them appear disconnected using the below command. Let’s assume that “AC powered” is ‘true’ meaning connected, and we want it to be disconnected -
    adb shell dumpsys battery set ac 0
    1. For “USB powered”, property key is usb.
    2. For “Wireless powered”, property key is wireless.
    3. On Android M+, these charging power sources can be disconnected in one command as below-
    adb shell dumpsys battery unplug
    Verify the above result using the command adb shell dumpsys battery. All the “AC powered/USB powered/Wireless powered” properties should now say ‘false’, as it is being disconnected.
  • Once disconnected, we can connect it back again by setting the power source property value to 1. Lets say we want the “AC powered” source to be connected.
    adb shell dumpsys battery set ac 1
    Verify the above result using the command adb shell dumpsys battery. The modified power source property should now say ‘true’, as it is being connected.
  • Once we start mocking the battery status, information from the hardware will no longer be used. Hence it is mandatory to run the below command at the end of the tests to revert the changes done.
    adb shell dumpsys battery reset
    If we had previously marked all the power sources to disconnected, then this command will revert it to what it was originally. So this can also be used to connect back the power source that was marked disconnected by us.
  • We have NOT done a perfect simulation of power source connect/disconnect here, since in the real world when a power source is disconnected from a device, the device starts to discharge; and when connected back to the power source, the device starts to charge. This charge and discharge affects the “status” property of the battery service. So, to complete the simulation in the proper way, one has to follow the below steps -

To Simulate Power source Connected/Disconnected on Emulator using the Extended Controls of the Virtual Device.

  • The proper and the easiest way to simulate the power source connect and disconnect, would be to use the battery control of the AVD provided in its extended controls as shown above.
  • On the same lines as with the ADB, follow the below guidelines for proper testing -

Sticky Broadcasts to test the Battery charging status

  • Sticky Broadcast Intents are intents that stay around even after the broadcast has finished.
  • ‘Intent.ACTION_BATTERY_CHANGED’ is one of the Sticky Broadcast Intents which contains information about the battery like charging state, plugged state, level and so on.
  • Sticky Broadcast Intents cannot be received through the components declared in the manifest. It can only be received by registering runtime with a call to the Context’s registerReceiver() method.
  • If we know that the Intent that we are registering with is a Sticky Broadcast Intent, then there is no need to pass anything for the receiver parameter. Passing Null in this causes the function to NOT register any receiver and returns the current instance of the Sticky Intent found.

Determining the current charging state on load of an app

  • This needs to be done in the onResume() method of the MainActivity, in other words, the app should be in the foreground.
  • The Battery charging status is obtained through the BatteryManager service. The BatteryManager class contains strings and constants used for the values in the ‘Intent.ACTION_BATTERY_CHANGED’, and provides methods for querying battery and charging properties.
  • BatteryManager Service is obtained from the Context’s getSystemService() passing the argument Context.BATTERY_SERVICE.
  • On Android Devices with Marshmallow and above, BatteryManager provides a method isCharging() that returns a boolean of the Charging status directly. It returns ‘True’ when the Battery is charging and it means that the device is plugged in and is supplying sufficient power that the battery level is going up (or the battery is fully charged). But there are certain caveats to how it reports the battery charging status (applicable to real devices also) -
    1. If the Battery level is equal to or above 90%, then it reports/considers the device to be charging even if it happens to go down by a level.
    2. If previously the Battery state was reported as Discharging, and now the battery level has gone up, then it updates the state to Charging.
    3. If previously the Battery state was reported as Charging, and the battery level is going down in spite of being connected to the power source, then it updates the state to Discharging owing to insufficient power supplied.

NOTE: The above order of conditions is respected while reporting the Battery charging state in Android Devices with Marshmallow and above. The Battery state updates are sent by an internal class BatteryStatsImpl.java. Following are the lines of code that report the same -

  • To read the battery status on pre-Android Marshmallow Devices,
    1. One has to first register the Sticky Broadcast ‘Intent.ACTION_BATTERY_CHANGED’, to get the current instance of that Sticky intent. This is done by calling the Context’s registerReceiver() method and passing Null to Receiver and the IntentFilter containing the sticky intent (‘Intent.ACTION_BATTERY_CHANGED’) we are interested in.
    2. From this Intent, get the integer extra data of the item ‘BatteryManager.EXTRA_STATUS’. This will contain the “status” property value of the Battery Service.
    3. Evaluate this value against the constants ‘BatteryManager.BATTERY_STATUS_CHARGING’, ‘BatteryManager.BATTERY_STATUS_FULL’ to determine whether the device is charging now or not.

NOTE: While testing on Android AVD through Emulator with the simulation explained, the current battery state will not be reflected (in the app) when launched immediately after its change. It takes certain time to reflect, so launch the app only after 30 seconds, since making the last mock update to power source state. This is not the case while testing on actual devices (in other words, does not apply to real devices).

Hope you liked it. Constructive comments always welcomed! 👍 😃

--

--

Kaushik N. Sanji

Was an IBM Sterling Commerce Consultant. Now an Android Rookie. Find my stuff here https://github.com/kaushiknsanji