It’s the responsibility of the developer to ensure the best user experience for the increasing number of mobile PCs being bought and used.
Every activity your application carries out can affect the power consumption of any computer. This article will help you understand the critical role your software can play in helping increase battery life and provide some places for you to get started.
Limited Power
CPUs have gotten faster, hard disks have gotten bigger and RAM has become more abundant in the computers we use, but one aspect of computing has not moved anywhere near as fast. Battery technology has not seen the same innovations as other areas of technology and still acts as a drawback to working on mobile computers.
The devices attached to the computer when it suspends may have changed by the time the computer resumes operations.
This limited improvement in battery life means that, as software developers, we need to think about how we can help our software’s users increase the length of time their mobile computers can be used before the battery is drained.
It may seem that there is little you can do to improve the battery life because it’s a hardware issue; at first, I felt this way too.
Surprisingly, software can have a huge affect on the lifetime of the battery. For example, scaling down functionality can drastically increase the lifetime of the battery in a mobile PC, as cana periodic timer for polling or background work.
Here are some key areas to consider for providing a better user experience to users running your software on mobile PCs: current power source (AC or DC), seamless suspend/resume transitions, use of the screen and graphics processor, use of the hard disk, use of the network, and use of other powered peripherals.
Consider an application that carries out some background tasks and performs regular saves of data to the hard disk. If the background task forces the CPU to step up, there is an increase in power consumption. Each time the hard disk is used, there is an increase in power consumption. Reducing the number of times the background tasks occur and increasing the time between data saves could help to improve battery life.
The first step to extending battery life is to discover the current power source.
Source of Power
In the Windows XP platform, there is an API function in Kernal32.dll that allows you to discover the current power source. This method is called GetSystemPowerStatus. GetSystemPowerStatus takes a reference to a SYSTEM_POWER_STATUS structure. To use this from managed code, it is a good idea to declare the structure as a class, as it will then be passed by reference. (See Listing 1.)
Using the GetSystemPowerStatus method creates an instance of a SYSTEM_POWER_STATUS class and passes it as the parameter to the method. When the method returns, you can examine the fields of the class to discover information on the current power source, as shown in Listing 2.
(On some devices, BatteryLifeTime and FullBatteryLifeTime fields may not be available. For this reason, it is often best to use the percentage of battery life remaining.)
Power Play
Once you know whether the computer is being powered from the battery or an AC power source, you can make some decisions as to how your application should behave. The big consumers of power on a mobile PC are the display, disk drives, and network.
Your application must never veto a suspend request.
The hard disk and display devices can power down on a Windows XP computer after a certain period of inactivity. There are Power Options settings in the control panel that allow you to adjust the time-out periods for these devices.
It would be useful if your application could detect whether a device has powered down and if so, not access it. If the power source is a battery, the application could cause the device to power up whether you want it to or not.
There is a Win32 function that you can use to determine whether a device is powered up or not. GetDevicePowerState takes two parameters: one to identify the device and the other that returns a Boolean indicating whether the device is powered up. This can be imported into managed code using the P/Invoke mechanism as follows:
[DllImport("Kernel32.DLL", CharSet = CharSet.Auto,
SetLastError=true)]
private extern static
bool GetDevicePowerState(
IntPtr hDevice,
out bool fOn);
This is a good way to find out if a disk is currently powered up. The method in Listing 3 shows how to retrieve information about whether the disk from which the assembly has been loaded is currently powered up.
GetDevicePowerState cannot be used to find out if the display monitor is currently powered up. This is an issue that has been solved in Windows Vista, as you will discover at the end of this article.
Notifications
Knowing the current power source is a good start, but your application then needs to be alerted when the power source changes. It is also important to be alerted when the computer is going to suspend or hibernate and when it wakes up or restores its state after a suspend or hibernate.
In the .NET Framework 1.1, there is a PowerModeChanged event in the SystemEvents class that can be discovered in the Microsoft.Win32 namespace.
The PowerModeChangedEvent passes you a PowerModeChangedEventArgs object containing a Mode property that is a PowerModes enumerated type.
The possible PowerModes you might receive are Resume, StatusChange, and Suspend.
It is obvious what the Resume and Suspend events indicate, but the StatusChange event can indicate any change in the current state of the power. The changes that might be indicated include a switch from AC to DC, a switch from DC to AC, or a change in the state of the battery (for example, indicating that the battery has only a low charge remaining).
When you receive a PowerModeChanged event with the StatusChange flag, it would be good practice to call the GetSystemPowerStatus method discussed earlier to find out what specifically has changed.
The PowerModeChanged event provides a subset of information that can be received from the WM_POWERBROADCAST Windows message. The whitepaper on MSDN (which can be found at http://msdn.microsoft.com/mobility/tabletpc/default.aspx?pull=/library/en-us/dntablet/html/mobpcpowmng.asp), discusses how to use the WM_POWERBROADCAST message, but for most applications, using the PowerModeChanged event should be sufficient.
Being Power Efficient
The user experience on a Mobile PC can be greatly improved by making sure your software does not consume more battery power than is needed. You can monitor this by carefully considering the requirements of your application when it runs on a computer-powered by battery.
To the end user, the resume experience should be seamless; it should appear as if the application is simply running again.
Some operations, such as audio output or graphics-intensive operations, are obvious power gluttons. Less obvious suspects are the impact that use of the hard disk and network have on battery life. In fact, all CPU operations have some impact on battery life and if your application is making excessive use of polling or background threads, these will also degrade performance of the battery.
Often, these are not purely technical decisions; the business decision makers need to be involved to understand the impact of requested features on battery life. It is good practice to avoid carrying out non-critical tasks on a computer that is currently powered only by battery.
Suspension of Operations
The consideration so far has been how to reduce power consumption when the computer is powered by batteries. There is one other important consideration to take into account: how your application should behave when the computer suspends. Suspending the computer is the primary way Windows helps extend mobile PC battery life when the computer is not in use.
There are two scenarios to consider that will cause the computer to go into suspend mode.
The first scenario is when the computer has been idle for a set time period and the Power Options are set for the PC to stand by or hibernate after that period of inactivity.
The second scenario is when the user shuts the lid or pushes the power button, which causes a request to suspend operations.
When the operating system suspends, it sends out the suspend notification message, which you can receive in the PowerModeChanged event. Your application then has to stop any limited cleanup actions and store any important state data as quickly as possible.
You should never request any user input on suspend. There is nothing more annoying to a user who closes the laptop’s lid to find that the computer did not suspend because your application was waiting for a response from a MessageBox and the battery has drained.
Once you have worked out how your application responds to a Suspend event, you should consider how it behaves upon receiving a Resume notification. To the end user, the resume experience should be seamless; it should appear as if the application is simply running again.
The devices attached to the computer when it suspends may have changed by the time the computer resumes operations. The computer may have been docked, undocked, had other peripherals changed or have a different network connectivity. Your application has to take account of this change and act accordingly, without interrupting the user or using a popup dialog box, if at all possible.
Your application must never veto a Suspend request. Once the computer has sent a notification to suspend, your application must comply. This becomes even more important in Windows Vista, where your application will be given a maximum of two seconds to respond to a Suspend notification before the operating system suspends, and vetoing a second request is not possible.
Although your application cannot do anything once the Suspend has been sent out, you may wish to prevent the computer from requesting a Suspend based on idle time.
For example, if your application is playing a video in a window, it is likely that while the user is watching that video, they will not be interacting with the computer. After a certain period of inactivity, the idle timer requests that the computer suspend. You can prevent the idle timer from triggering a request to suspend by using the Win32 SetThreadExecutionState API. You should only do this when it is absolutely necessary, as it will cause degradation in battery life.
As the name suggests, SetThreadExecustionState is per thread. The operating system maintains a count of threads that have requested a certain state, like perhaps the display is required. Only when each of these threads has indicated that they no longer require the resource will the resource be allowed to suspend. SetThreadExecutionState does not prevent the system from suspending when it is user-initiated.
The SetThreadExecutionState function and the EXECUTION_STATE types can be defined in C# as shown in the code snippet below:
[FlagsAttribute]
public enum EXECUTION_STATE :uint
{
ES_SYSTEM_REQUIRED = 0x00000001,
ES_DISPLAY_REQUIRED = 0x00000002,
// legacy flag should not be used
// ES_USER_PRESENT = 0x00000004,
ES_CONTINUOUS = 0x80000000,
}
[DllImport("Kernel32.DLL", CharSet = CharSet.Auto,
SetLastError=true)]
private extern static
EXECUTION_STATE SetThreadExecutionState(
EXECUTION_STATE state);
Power On Windows Vista
Windows Vista improves how your application can interact with Windows’ power management. The WM_POWERBROADCAST message is still sent to notify the application of changes. This does mean that there needs to be a Window handle to receive these messages.
Windows Vista improves how you can interact with the power management APIs.
Windows Vista then provides a number of GUIDs that you can use to register to receive information about particular features of the power. For example, if you want your application to be notified of the power source, register for the GUID_ACDC_POWER_SOURCE GUID.
The registration method is called RegisterPowerSettingNotification and takes a GUID parameter that identifies the notification for which you are registering.
hPowerSrc =
RegisterPowerSettingNotification(this.Handle,
ref GUID_ACDC_POWER_SOURCE,
DEVICE_NOTIFY_WINDOW_HANDLE);
The returned handle should then be used to unregister the notification.
UnregisterPowerSettingNotification(hPowerSrc);
When the notification messages are sent, they are accompanied by a structure that is passed in the lParam of the Windows message. This structure is a POWERBROADCAST_SETTING, defined here in C#.
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct POWERBROADCAST_SETTING
{
public Guid PowerSetting;
public Int32 DataLength;
}
The PowerSetting field in the structure identifies the setting or event notification you are receiving. Remember, you can register to be notified of changes to multiple power settings.
In Windows Vista, it is easy for the user to change the active Power Plan. A Power Plan specifies the power management settings in effect on the computer. The three power plans that will ship with Windows Vista are Automatic, High Performance, and Power Saver.
As the names suggest, these power plans indicate to the running application how to behave regarding power consumption. In Automatic mode, it is best practice to scale functionality based on criteria, such as application demand, current power source and battery percentage remaining. In High Performance, your application should not scale back functionality to improve battery life; the user has chosen not to worry about battery life and your application should focus on providing the best possible performance. When the user chooses Power Saver, your application should scale back all non-critical functionality to reduce power consumption.
Your application can register for a change in the Power Plan by calling the RegisterPowerSettingNotification method using the GUID_POWERSCHEME_PERSONALITY GUID.
Conclusion
As you can see, it’s wise to add power management to your application. Doing this is not hard and can help to improve the user experience by extending the battery life of the device on which your application is run.
To prevent the user experiencing a drained battery, your application should always respond in a timely manner to Suspend requests and never block the computer from suspending.
Here is a list of some of the things you should consider when building your application:
- Determine the power source.
- Never prevent a request for the system to suspend.
- When the system resumes, your application should run smoothly, as if it had never been interrupted.
- When the system resumes, the available devices and power source may have changed. Your application should be reasonably unaffected by these changes or have built-in contingency plans.
- Reduce the power consumption of your application when the power source is a battery by:
- Avoiding polling operations
- Not running non-critical worker threads
- Avoiding unnecessary device (disk, display, Bluetooth, USB) access