Creating Power Aware Applications on Linux* using Qt4


Download PDF

Creating Power Aware Applications on Linux using Qt4 [PDF 51KB]


Background

The information provided in this paper is relatively simple, with the intent of giving the reader a stepping stone to pursuing these examples further and expanding them into his own application. The references given at the end are a tremendous resource and should be consulted before any action is taken. All of the technologies used in this paper are under open source licenses and under constant revision and development. For the best results, read the latest documentation available.

Before getting started, since we will be gathering information from the system’s battery, it would be most inconvenient if we were developing on a desktop system without a battery. Fortunately, there exists a program for creating a fake battery on HAL whose purpose is exactly this - to give developers the ability to write power aware applications without developing on a system with a battery. This new tool developed by Intel, called the Power Status Simulator [1] will be available in April 2008.


A Brief Description of D-Bus

In order for any application to communicate with the system, the system first needs a channel through which it can communicate. For the purpose of this paper, we will be using D-Bus, a low-level interprocess communication (IPC) channel designed to help applications communicate. While D-Bus is a very generic interface with numerous use cases, the focus of this paper is to obtain information on hardware devices, and we will use the D-Bus communication channel to gather system information from the Linux Hardware Abstraction Layer (HAL).

The D-Bus daemon has two communication “busses”; a “system” bus, which is available to all open sessions on the system, and a “session” bus which is available to each open session on the system. Because the system hardware is shared between all users on a system (in most cases), all of the hardware information is shared over the system bus channel.

There is much more detail on D-Bus available through the freedesktop.org website. [2]


A Brief Description of HAL

To put it simply, HAL is exactly as it sounds, a layer which abstracts the hardware so that hardware properties are much more accessible to applications. Any hardware device which is registered in HAL has two properties: category and capabilities. The category describes what the device is: a USB storage device, a camera, etc., while the capabilities describe what each device can do. A single device may have multiple properties, for example, an MP3 player can often be used as a generic storage device.

Since our application is interested in battery power and AC power, it is most important to look at those devices that have the battery and ac_adapter properties. Later in this paper, we will discover how we can gather information from devices that have these properties.

Much more informat ion is available through the freedesktop.org website. [3]


A Brief Description of Trolltech Qt4

Qt is a C++ development kit used to create applications that are cross platform and feature rich. To call it a GUI toolkit would be limiting, as it provides libraries to simplify development for almost any type of desktop application. As of Qt 4, the toolkit also has an open source (GPL) version for several platforms, including Microsoft Windows, Mac OS X, and Linux. More details on Qt can be found on Trolltech’s website at http://trolltech.com.

For this paper, we will assume that the user is familiar with the basics of Qt development. All examples used in this paper were developed and executed using Qt version 4.3.3, and some features described may not be available in previous versions. [4] Check the documentation for your version for more details.


Talking to HAL through D-Bus

The primary method of communicating with HAL is through D-Bus. Referring to the latest HAL specification [5] we see that HAL provides a few key interfaces on D-Bus through which we can gather information about the system’s hardware.

At the highest level, HAL provides the org.freedesktop.Hal.Manager interface, which is implemented by the /org/freedesktop/Hal/Manager object. This object’s primary use is for discovering objects on the system. By using the object, an application can query its FindDeviceByCapability method, which takes a string as a parameter and returns an array of object references.

Now that the application has all of the devices in which we are interested, it can query the properties for each of those devices. All HAL devices, each of which is prefixed with /org/freedesktop/Hal/devices/, implement the interface provided by HAL: org.freedesktop.Hal.Device. Our application is most interested in the GetProperty method, which takes a string containing the property in which the application is interested and returns the value contained in the property. Of course, exceptions can be thrown if the property does not exist but for the purposes of this tutorial, we will not concern ourselves with exception handling.

Of perhaps even more interest to the application are the signals that are emitted by the object under /org/freedesktop/Hal/devices/. A signal is an asynchronous event that may occur in hardware, or simply be a software based event. An application can register with the signal that is emitted, and receive notification of an event. By receiving notifications, our application does not have to resort to expensive polling loops which can burden both the CPU and battery power on a system. Luckily for us, the HAL developers have created a PropertyChanged signal, which is emitted anytime a property in a device is modified. For our application, we can receive notification that the system has gone from AC to DC power, or the opposite. We can also be notified when the battery reaches a critical state, so our application can take any precautions necessary before the system shuts down. An application may also want to throttle down performance when on battery power, or delay unnecessary and expensive IO operations until a later time.


Bringing it all together

Now that we have a basic understanding of everything we will be using, let’s examine a basic example of Qt 4, D-Bus and HAL in action. Our simple application will look for all of the batteries on the system, listen for any property changes, and simply output what was received.

In order to utilize Qt’s signal/slot mechanism, we must first create an object that inherits from QObject. Our object will be very simple, with a default constructor, and a single slot, used to connect to the signal emitted by the PropertyModified signal. Looking at the latest HAL specification, the parameters from the signal include an integer with the number of changes, as well as an array (or QList) containing a structure. The structure is included in our object described below:

#ifndef HALOBJECT_H  

#define HALOBJECT_H  


#include <QObject> 

#include <QString> 

#include <QDebug>  


/* Structure required by the PropertyModified signal */

struct Property {

QString name;  

bool added;  

bool removed;  

}; 


class HalObject : public QObject

{ 

Q_OBJECT 


public: 

HalObject(QObject * parent = 0); 

virtual ~HalObject() {}  

public slots:  

void listen(int num, QList<Property> prop);  

};


#endif

 

The HalObject’s only slot, void listen(int num, QList<Property> prop), matches the signal’s signature.

Before we are able to connect the application to D-Bus to talk to HAL, there is a bit of work that needs to be done beforehand. Because Qt’s D-Bus bindings have no knowledge of HAL, and the types it returns, we must register any “new” types before they can be transmitted in a signal/slot connection. The signal emitted by HAL includes a QList of Property structs. The first step is to declare the new metatypes Property and QList<Property>:

#include "HalObject.h" 

/* Declare a new type for DBus to handle */  

Q_DECLARE_METATYPE(Property)

Q_DECLARE_METATYPE(QList<Property>)

 

This step registers Property as a metatype, so that it can be used in a QList. We are still not in the clear to use it as a D-Bus argument. In order to achieve this, we need to define the two operators “<<” and “>>” for the Property struct:

/* DBus arguments need to handle the << and >> operators */ 

const QDBusArgument & operator<<

(QDBusArgument &arg, const Property &change) 

{

arg.beginStructure(); 

arg << change.name << change.added << change.removed; 

arg.endStructure(); 

return arg; 

}


const QDBusArgument & operator>>

(const QDBusArgument &arg, Property &change) 

{

arg.beginStructure(); 

arg >> change.name >> change.added >> change.removed; 

arg.endStructure(); 

return arg; 

}

 

After this step, we can register the new metatypes with qdbus at runtime:

int main(int argc, char** argv) 

{

/* Register previously declared types with DBus */ 

qDBusRegisterMetaType< Property >(); 

qDBusRegisterMetaType< QList<Property> >();

 

We may now create our object which will be used to react to any PropertyModified events emitted by HAL. The application also, for convenience, creates a large list containing all of the possible properties for any device with battery capabilties. All of these properties are listed in the latest HAL specification [5].

/* This object is used to listen for and react to events */ 

HalObject * halObj = new HalObject(); 


/* All of the possible properties for batteries in HAL */ 

QStringList properties; 

properties << 

"battery.present" << 

"battery.type" << 

"battery.charge_level.unit" << 

"battery.charge_level.design" << 

"battery.charge_level.last_full" <<
 
"battery.charge_level.current" << 

"battery.charge_level.rate" << 

"battery.charge_level.warning" << 

"battery.charge_level.low" << 

"battery.charge_level.granularity_1" << 

"battery.charge_level.granularity_2" << 

"battery.reporting.unit" << 

"battery.reporting.design" << 

"battery.reporting.last_full" << 

"battery.reporting.current" << 

"battery.reporting.rate" << 

"battery.reporting.warning" << 

"battery.reporting.low" << 

"battery.reporting.granularity_1" << 

"battery.reporting.granularity_2" << 

"battery.charge_level.capacity_state" << 

"battery.voltage.unit" << 

"battery.voltage.design" << 

"battery.voltage.current" << 

"battery.alarm.unit" << 

"battery.alarm.design" << 

"battery.remaining_time" << 

"battery.remaining_time.calculate_per_time" << 

"battery.charge_level.percentage" << 

"battery.is_rechargeable" << 

"battery.rechargeable.is_charging" << 

"battery.rechargeable.is_discharging" << 

"battery.command_interface" << 

"battery.vendor" << 

"battery.model" << 

"battery.reporting.technology" << 

"battery.technology" << 

"battery.serial";

 

The first thing our program must do in order to communicate with HAL is to make a connection to D-Bus. Our application will use the system bus to connect to HAL, as HAL is a system daemon and available to all sessions on the system. After getting a handle to the system bus, we can open a connection through which we can talk to the /org/freedesktop/Hal/Manager object.

/* HAL stores everything on the system bus */ 

QDBusConnection conn = QDBusConnection::systemBus(); 


/* Connect to the HAL Manager device to find all devices */ 

QDBusInterface hal("org.freedesktop.Hal", 

"/org/freedesktop/Hal/Manager", 

"org.freedesktop.Hal.Manager", 

conn);

 

Assuming opening the connection went smoothly, we can now make method calls to the org.freedesktop.Hal.Manager object. Our application is only interested in finding all of the devices that have the battery capability. To do this, we pass two QStrings to our open interface’s call method, the first being the name of the method we want to call, and the second being the argument to pass to the method call. This would be the equivalent of calling:

(/org/freedesktop/Hal/Manager).FindDeviceByCapability("battery")

What gets returned from this call is essentially a QList of strings containing the device UDI, but because of the flexible nature of D-Bus types, these are all wrapped in a QVariant type, so we need to pull the QStrings out ourselves. Once we have a device’s UDI, we can then open a connection to the device in order to query for any properties.

/* Call the FindDeviceByCapability method, passing in ’battery’ 

* to find all devices with the battery capability 

*/

QDBusMessage msg = hal.call("FindDeviceByCapability", "battery"); 


/* Assuming the call worked, the device udis are returned as an array 

* of QStrings, wrapped in a QVariant 

*/

QList<QVariant> devices = msg.arguments(); 


/* Iterate through each device udi returned from the previous call */ 

foreach (QVariant name, devices) 

{

QString bat = name.toStringList()[0]; 

qDebug() << "Found device:&

nbsp;" << bat; 

/* open a connection to the individual battery */ 

QDBusInterface device("org.freedesktop.Hal", 

bat, 

"org.freedesktop.Hal.Device", 

conn);

 

We have opened a connection with the battery device, which we will use in a bit to query for its current state. Before we proceed, we make a signal/slot connection on our battery device. Using the device’s connection, we use the connect() method, which is similar to that of the standard QObjects. The first argument is a QString containing the D-Bus service, which in HAL’s case is ‘‘org.freedesktop.Hal’’. The second is a QString containing the object, which we have stored in the bat variable, and would have the form ‘‘/org/freedesktop/Hal/devices/devicename’’. The third argument is the object’s interface, which for any HAL device is ‘‘org.freedesktop.Hal.Device’’. The fourth argument is another QString with the name of the signal that is emitted by the battery object, which in our case is going to be ‘‘PropertyModified’’. The last two arguments are identical to the last two arguments to the standard QObject.connect() method call, the first being the object to which we want to connect (our HalObject) and the last being the object’s slot, along with the SLOT() macro. We are able to use our new metatype in the slot argument since we have already registered it with the qdbus module. Had we not, we would get an error.

/* Listen for any ’PropertyModified’ events from this battery 

* on DBus 

*/

bool success = device.connection().connect("org.freedesktop.Hal", 

bat,

"org.freedesktop.Hal.Device", 

"PropertyModified",

halObj, 

SLOT(listen(int,QList<Property>))); 

qDebug() << "SUCCESS : " << success;  

 

In the last part of our simple program, we simply make a call the to the battery’s GetProperty method, passing in each one of the possible battery properties that we had declared earlier. Because it is not a requirement that a battery has every one of the properties, we need to check the return value of the reply before we can proceed, otherwise we may crash our program. The final part of the program is the standard Qt application execution loop, in which our program will listen for any PropertyModified events emitted by each battery on the system.

/* On this battery, call the GetProperty method for each battery 

* property listed above.  Some do not exist, so we must check 

* for any errors, otherwise, we can crash. 

*/ 

foreach (QString p, properties) 

{

msg = device.call("GetProperty", p); 

QVariant var = msg.arguments()[0]; 

switch(msg.type()) 

{
 
case QDBusMessage::MethodCallMessage: 

qDebug() << "Method Call Return: " << msg; 

break;

case QDBusMessage::ReplyMessage: 

qDebug() << "Reply: " << p << ": " << var.toString(); 

break;

case QDBusMessage::ErrorMessage: 

qDebug() << "Error Message: " << msg.errorMessage(); 

break;

case QDBusMessage::InvalidMessage: 

qDebug() << "Invalid Message: " << msg; 

break;

case QDBusMessage::SignalMessage: 

qDebug() << "Signal Message: " << msg; 

break; 

}

}

}

QApplication app(argc,argv); 

return app.exec(); 

}

 

This simple program has been able to make a connection to HAL, query all of the batteries on the system, print all of the battery properties, and listen for any changes that have occurred in the battery, and react accordingly. Our reaction is quite simple; we simply output the property that was modified. While our example is quite simple, it provides a foundation on which we can build robust platform awareness.

Using these same principles and techniques, we can be aware of the state of any device that is registered with HAL. Please refer to the latest HAL specification [5] for all of the supported devices and properties.


Conclusion

By utilizing the functionality of Qt 4, HAL and D-Bus, Linux developers can create applications which are aware of the power context of the platform on which they are running. By keeping informed, an application can be a “good citizen” and reduce its overall power footprint on the system.


References

[1] J. Olivas, T. Darwish, and J. Hartley. (2008) Application Energy Toolkit. http://software.intel.com/en-us/articles/application-energy-toolkit.
[2] (2008) freedesktop.org - software/dbus. World Wide Web. http://www.freedesktop.org/wiki/Software/dbus.
[3] (2008) freedesktop.org - software/hal. World Wide Web. http://www.freedesktop.org/wiki/Software/hal.
[4] Trolltech. (2008) Qt Reference Documentation (Open Source Edition). World Wide Web. http://doc.trolltech.com/4.3/index.html.
[5] D. Zeuthen. HAL 0.5.10 Specification. World Wide Web. http://www.marcuscom.com/hal-spec/hal-spec.html.


About the Author

Joe Olivas is an Intel Software Engineer working on Platform Power Enabling as part of client enabling in the Software Solutions Group. His current focus is on defining tools and technologies to support the development of energy-efficient software for Intel-based mobile platforms. He obtained his Masters degree in Computer Science from California State University, Sacramento. His email is joseph.k.olivas@intel.com


Reportez-vous à notre Notice d'optimisation pour plus d'informations sur les choix et l'optimisation des performances dans les produits logiciels Intel.