Home · All Namespaces · All Classes · Grouped Classes · Modules · Functions |
This document describes how to integrate a third-party Voice over IP (VoIP) agent into Qt Extended. However large parts of this guide refer to general telephony interfaces and classes. This means that this document also describes the essential steps to integrate an arbitrary telephony service.
Third parties need to supply at least three components as follows:
To demonstrate the process, this document describes how to integrate the IAX2 protocol used by the Asterisk telephony server. We will use the open source iaxclient library to provide the IAX2 protocol implementation. The full source code for the Asterisk integration can be found in the Qt Extended source tree under examples/asterisk.
Note that Asterisk does not support presence.
In the examples below, the virtual keyword will be shown on methods that must be overridden from a parent class. If a method does not have the virtual keyword, then it is unique to the subclass.
The integration model for VoIP agents uses the Phone Library to provide the basic infrastructure. The phone library divides up all telephony functionality into services and interfaces.
Handlers in the telephony system are referred to as services. Each service has a unique name, such as modem, voip, etc. By convention, service names are lower case. Within each service is a list of interfaces for functionality areas such as network registration, phone calls, presence, SMS, etc.
Interface names correspond to class names in the Qt Extended Telephony API. For example, the QNetworkRegistration interface provides access to network registration features, and the QPresence interface provides methods to access user presence information.
A more thorough overview of the library can be found on the Phone Library page in the documentation.
Service names should be unique. The name modem is reserved for use by GSM modems, and the name voip is reserved for SIP-based VoIP services. The example in this document uses the name asterisk.
To integrate Asterisk, we need to provide a telephony service called asterisk which inherits from the QTelephonyService class. The most basic definition is as follows (from iaxtelephonyservice.h):
class IaxTelephonyService : public QTelephonyService { Q_OBJECT public: IaxTelephonyService( const QString& service, QObject *parent = 0 ); ~IaxTelephonyService(); virtual void initialize(); ... };
The constructor and destructor initialize and shut down the iaxclient library (for the full details, see the source code in the examples/asterisk/iaxagent directory):
IaxTelephonyService::IaxTelephonyService ( const QString& service, QObject *parent ) : QTelephonyService( service, parent ) { // Initialize the iaxclient library ... } IaxTelephonyService::~IaxTelephonyService() { // Shut down the iaxclient library ... }
All telephony services must override the initialize() method to create the telephony interfaces that it requires. For Asterisk, we need the QNetworkRegistration, QTelephonyConfiguration, QServiceChecker, and QPhoneCallProvider interfaces:
void IaxTelephonyService::initialize() { if ( !supports<QNetworkRegistration>() ) addInterface( new IaxNetworkRegistration( this ) ); if ( !supports<QTelephonyConfiguration>() ) addInterface( new IaxConfiguration( this ) ); if ( !supports<QServiceChecker>() ) addInterface( new IaxServiceChecker( this ) ); if ( !callProvider() ) setCallProvider( new IaxCallProvider( this ) ); QTelephonyService::initialize(); }
The telephony service must call the base QTelephonyService::initialize() function to complete the initialization process.
When VoIP telephony services are started, they receive a QCop message Telephony::start() on their application channel. When they are shut down, they receive a QCop message Telephony::stop() on their application channel. For more information on QCop service messages, see the Qt Extended Services documentation.
We can intercept these messages using the QtopiaAbstractService class:
class IaxTelephonyServiceQCop : public QtopiaAbstractService { Q_OBJECT public: IaxTelephonyServiceQCop( QObject *parent = 0 ); ~IaxTelephonyServiceQCop() {} public slots: void start(); void stop(); private: IaxTelephonyService *service; }; IaxTelephonyServiceQCop::IaxTelephonyServiceQCop( QObject *parent ) : QtopiaAbstractService( "Telephony", parent ) { publishAll(); service = 0; } void IaxTelephonyServiceQCop::start() { if ( !service ) { // Register a task to keep us alive while the service is running. QtopiaApplication::instance()->registerRunningTask ( "IaxTelephonyService", this ); // Create the service handler, registered under the name "asterisk". qLog(VoIP) << "Starting iaxclient service handler"; service = new IaxTelephonyService( "asterisk", this ); service->initialize(); } } void IaxTelephonyServiceQCop::stop() { if ( service ) { // Delete the service handler. qLog(VoIP) << "Stopping iaxclient service handler"; delete service; service = 0; // Deregister the task which allows this daemon to shut down. QtopiaApplication::instance()->unregisterRunningTask ( "IaxTelephonyService" ); } }
It is important that the agent register a running task using QtopiaApplication::registerRunningTask() so that Qt Extended will not shut down the agent prematurely. It is also important to create the telephony service properly, and to not forget the call to initialize():
service = new IaxTelephonyService( "asterisk", this ); service->initialize();
If you forget the call to initialize(), the service will not be registered with the system and phone calls will not work.
The final coding step is to start the QCop service from the agent's main function. See main.cpp in the examples/asterisk/iaxagent directory for the details.
We now need to register the iaxagent daemon as a Qt Extended telephony service by adding an iaxagent to the directory $QPEDIR/services/Telephony whose contents is as follows:
[Extensions] [Standard] Version = 100
Now when Qt Extended starts up, it will detect the new telephony service and send it the Telephony::start() QCop message.
When a telephony service starts up, it may not be ready to register to the network immediately. Some time may be required to properly initialize the service, or the service may not be properly configured.
The Qt Extended server uses the QServiceChecker interface to determine if the service is ready to be used. If the telephony service does not provide an implementation of QServiceChecker, then Qt Extended will assume that the service is ready to use as soon as initialize() returns.
For our Asterisk service, we need to wait for the registration server to be configured in the Asterisk.conf file before it can be used. So we have to implement a new service checker as follows (the actual code is in iaxservicechecker.h and iaxservicechecker.cpp):
class IaxServiceChecker : public QServiceChecker { Q_OBJECT public: explicit IaxServiceChecker( IaxTelephonyService *service ); ~IaxServiceChecker() {} public slots: void updateRegistrationConfig(); }; IaxServiceChecker::IaxServiceChecker( IaxTelephonyService *service ) : QServiceChecker( service->service(), service, Server ) { updateRegistrationConfig(); } void IaxServiceChecker::updateRegistrationConfig() { QSettings config( "Trolltech", "Asterisk" ); config.beginGroup( "Registration" ); if ( config.value( "Server" ).toString().isEmpty() ) setValid( false ); else setValid( true ); }
Now, we can call updateRegistrationConfig() whenever the configuration file is updated and it will notify Qt Extended of whether the Asterisk service can be used or not.
Once a telephony service has been successfully initialized and checked, the next step is to register to the network. Qt Extended uses the QNetworkRegistration interface to control the network registration state of telephony services.
There are two general modes that can be used by a telephony service for network registration: automatic and manual. In automatic mode, the telephony service will attempt to register to the network as soon as possible after service initialization. In manual mode, the user must explicitly launch the appropriate settings program and request network registration.
For Asterisk, we override the QNetworkRegistration interface to create the IaxNetworkRegistration class (the actual code is in iaxnetworkregistration.h and iaxnetworkregistration.cpp):
class IaxNetworkRegistration : public QNetworkRegistrationServer { Q_OBJECT public: explicit IaxNetworkRegistration( IaxTelephonyService *service ); ~IaxNetworkRegistration(); public slots: virtual void setCurrentOperator ( QTelephony::OperatorMode mode, const QString& id, const QString& technology ); virtual void requestAvailableOperators(); void registerToServer(); void deregisterFromServer(); ... private: bool pendingSetCurrentOperator; }; IaxNetworkRegistration::IaxNetworkRegistration( IaxTelephonyService *service ) : QNetworkRegistrationServer( service->service(), service ) { pendingSetCurrentOperator = false; } IaxNetworkRegistration::~IaxNetworkRegistration() { deregisterFromServer(); } void IaxNetworkRegistration::setCurrentOperator ( QTelephony::OperatorMode mode, const QString&id, const QString& technology) { if ( mode == QTelephony::OperatorModeDeregister ) { pendingSetCurrentOperator = true; deregisterFromServer(); } else { pendingSetCurrentOperator = true; registerToServer(); } } void IaxNetworkRegistration::requestAvailableOperators() { QList<QNetworkRegistration::AvailableOperator> list; emit availableOperators( list ); } void IaxNetworkRegistration::registerToServer() { ... } void IaxNetworkRegistration::deregisterFromServer() { ... }
The actual code that performs the registration and deregistration is not shown here. See the source code for the full details. The important point to note is that once the registration has completed, or been lost, the Asterisk service should call QNetworkRegistrationServer::updateRegistrationState() to update the registration state to one of the following values:
QTelephony::RegistrationNone | Registration is not currently available. |
QTelephony::RegistrationHome | The user is registered to their home network. |
QTelephony::RegistrationSearching | The service is attempting to register but has not done so yet. |
QTelephony::RegistrationDenied | The service tried to register but was denied, probably because the user's authentication credentials were invalid. |
QTelephony::RegistrationUnknown | The registration state is unknown. This normally only makes sense for GSM networks. For VoIP implementations, use QTelephony::RegistrationNone instead. |
QTelephony::RegistrationRoaming | The user is registered to a network and can place calls, but it is not their usual home network. |
If the Asterisk service is set to the automatic registration mode, then these registration state changes will happen shortly after service initialization. If the service is set to the manual registration mode, then these registration state changes will only happen after the user launches the settings program and manually requests registration.
The other main component of network registration is the list of available network operators. This is used by settings programs to allow the user to choose an alternative network in their current location.
For Asterisk, there is only a single operator: the one configured by the user in the Asterisk.conf file. It therefore doesn't make sense to allow the user to choose another operator and we return an empty list from requestAvailableOperators().
For other VoIP implementations, especially those that can roam between public WiFi hotspots, it may make sense to return a non-empty list from requestAvailableOperators(), giving the user to ability to choose from a list of the networks that are in range.
Once the service has been checked, and the network registered, the next step is to enable the placement of phone calls. Qt Extended achieves this in telephony services with the QPhoneCallProvider interface. Our Asterisk agent needs to inherit from QPhoneCallProvider and override the create() method (iaxcallprovider.h and iaxcallprovider.cpp):
class IaxCallProvider : public QPhoneCallProvider { Q_OBJECT public: IaxCallProvider( IaxTelephonyService *service ); ~IaxCallProvider() {} void stateEvent( struct iaxc_ev_call_state *e ); protected: virtual QPhoneCallImpl *create ( const QString& identifier, const QString& callType ); }; IaxCallProvider::IaxCallProvider( IaxTelephonyService *service ) : QPhoneCallProvider( service->service(), service ) { setCallTypes( QStringList( "Asterisk" ) ); ... } QPhoneCallImpl *IaxCallProvider::create ( const QString& identifier, const QString& callType ) { return new IaxPhoneCall( this, identifier, callType, -1 ); }
The call to QPhoneCallProvider::setCallTypes() in the constructor is very important. It publishes the call types that are understood by the telephony service into the value space. Qt Extended uses this information to select an appropriate service to place an outgoing call. If the setCallTypes() function is not called in the constructor, then it will not be possible to place phone calls using the service.
Call types are typically simple names such as Voice, VoIP, Data, Fax, Video, etc. See the documentation for QPhoneCallManager::create() for a description of how call types are used to select an appropriate telephony service.
The VoIP call type is reserved for use by SIP-based telephony services. We cannot use that for our IAX2 implementation, so we publish the service's call type as Asterisk instead.
By convention, service names start with a lower-case letter, and call type names start with an upper-case letter. This convention is not strictly enforced by Qt Extended, but it can help when diagnosing telephony problems to be able to distinguish a word used as a service name and the same word used as a call type.
This code creates an instance of IaxPhoneCall whenever the user attempts to place an outgoing phone call. It will then be followed by a call to IaxPhoneCall::dial(). The IaxPhoneCall class is declared as follows:
class IaxPhoneCall : public QPhoneCallImpl { Q_OBJECT public: IaxPhoneCall( IaxCallProvider *provider, const QString& identifier, const QString& callType, int callNo ); virtual ~IaxPhoneCall(); virtual void dial( const QDialOptions& options ); virtual void hangup( QPhoneCall::Scope scope ); virtual void accept(); virtual void hold(); virtual void activate( QPhoneCall::Scope scope ); virtual void tone( const QString& tones ); virtual void transfer( const QString& number ); void stateEvent( struct iaxc_ev_call_state *e ); ... };
All telephony services must inherit from QPhoneCallImpl to provide the functions on phone calls. See the documentation for that class for more information on the functionality that is required.
The above applies to outgoing calls. For incoming calls, the Asterisk service detects the call in IaxCallProvider::stateEvent() and then constructs an instance of IaxPhoneCall directly:
void IaxCallProvider::stateEvent( struct iaxc_ev_call_state *e ) { IaxPhoneCall *call = fromCallNo( e->callNo ); if ( call ) { // State change on a known call. call->stateEvent( e ); } else if ( ( e->state & IAXC_CALL_STATE_RINGING ) != 0 ) { // Newly arrived incoming call. beginStateTransaction(); QString identifier = QUuid::createUuid().toString(); IaxPhoneCall *call = new IaxPhoneCall ( this, identifier, "Asterisk", e->callNo ); call->setNumber( e->remote ); call->setActions( QPhoneCallImpl::Transfer ); call->setState( QPhoneCall::Incoming ); endStateTransaction(); } }
While the IAX2 protocol does not support presence, most other VoIP protocols do. Presence can be handled by inheriting from QPresence:
class SipPresence : public QPresence { Q_OBJECT public: SipPresence( SipTelephonyService *service ); ~SipPresence(); public slots: virtual bool startMonitoring( const QString& uri ); virtual bool stopMonitoring( const QString& uri ); virtual void setLocalPresence( QPresence::Status status ); ... };
The VoIP agent must also add two lines to the initialize() function to create the presence interface at startup time:
if ( !supports<QPresence>() ) addInterface( new SipPresence( this ) );
See the documentation for QPresence for more information on the required functionality for the presence interface.
Configuration of VoIP services can be very complicated, which is why we recommend that you write a separate settings program that asks the user for the relevant details and then writes them to a configuration file that the agent daemon can access. For the Asterisk example, the source code for its settings program can be found under examples/asterisk/iaxsettings.
Once the configuration file has been updated, it is necessary to notify the agent daemon that it needs to reload the configuration. If your VoIP agent already has a way of performing this notification, then you can stop here. Or you can use the QTelephonyConfiguration interface that Qt Extended provides.
Our Asterisk agent daemon understands two configuration messages from the iaxsettings program: registration and callerid. The former changes the registration settings, and the latter changes the user's caller-id (name and number) information. We implement this in the IaxConfiguration class (iaxconfiguration.h and iaxconfiguration.cpp):
class IaxConfiguration : public QTelephonyConfiguration { Q_OBJECT public: IaxConfiguration( IaxTelephonyService *service ); ~IaxConfiguration() {} public slots: virtual void update( const QString& name, const QString& value ); virtual void request( const QString& name ); private: IaxTelephonyService *service; }; IaxConfiguration::IaxConfiguration( IaxTelephonyService *service ) : QTelephonyConfiguration( service->service(), service, Server ) { this->service = service; } void IaxConfiguration::update( const QString& name, const QString& ) { // Process messages from the "iaxsettings" program for config updates. if ( name == "registration" ) service->updateRegistrationConfig(); else if ( name == "callerid" ) service->updateCallerIdConfig(); } void IaxConfiguration::request( const QString& name ) { // Not supported - just return an empty value. emit notification( name, QString() ); }
There are two other forms of configuration that can be handled in a special manner: network registration changes and presence changes. The settings program can use QNetworkRegistration::setCurrentOperator() to register to and deregister from the network, and it can use QPresence::setLocalPresence() to update the user's presence state.
It is not a requirement that you use these configuration API's. If your VoIP agent has some other method for changing configuration settings, you are free to use that instead.
New VoIP services need some user interface support in the server to complete the integration. They do this by creating a server task that inherits from QAbstractCallPolicyManager.
The call policy manager tells Qt Extended when the service is active, when network registration changes occur, what call type to use for outgoing call requests, and the icons to display to represent network registration and call types. For Asterisk, the call policy manager is declared as follows (asteriskmanager.h in the server sources):
class AsteriskManager : public QAbstractCallPolicyManager { Q_OBJECT public: AsteriskManager( QObject *parent=0 ); ~AsteriskManager(); virtual QString callType() const; virtual QString trCallType() const; virtual QString callTypeIcon() const; virtual QTelephony::RegistrationState registrationState() const; virtual QAbstractCallPolicyManager::CallHandling handling(const QString& number); virtual bool isAvailable(const QString& number); virtual QString registrationMessage() const; virtual QString registrationIcon() const; ... }; QTOPIA_TASK_INTERFACE(AsteriskManager);
The most important function is QAbstractCallPolicyManager::handling(). This helps the server choose an appropriate telephony service when placing an outgoing call (incoming calls are handled implicitly by the service that first announced them). The handling() function inspects the phone number to determine if it can handle it at present. For Asterisk, the code is as follows (asteriskmanager.cpp):
QAbstractCallPolicyManager::CallHandling AsteriskManager::handling (const QString& number) { // Cannot handle URI's that contain '@' or ':'. if (number.contains(QChar('@')) || number.contains(QChar(':'))) return CannotHandle; // If no network registration, then cannot handle at this time. if (registrationState() != QTelephony::RegistrationHome) return CannotHandle; // Assume that this is a number that we can dial. return CanHandle; }
We first filter out URI's for SIP and other call types that do not use phone numbers. Then we check to see if the Asterisk service is currently registered to the user's home network. If it is, then we indicate that Asterisk can handle the call.
The return value is used by Qt Extended to select the appropriate telephony service to make the call. If two or more call policy managers return CanHandle, then the user will be presented with a list to choose from. In the case of Asterisk, this may happen if the user is registered to both Asterisk via WiFi and a GSM cellular network at the same time.
The allowable return values and their meanings are as follows:
QAbstractCallPolicyManager::CannotHandle | This telephony service cannot handle the requested number. |
QAbstractCallPolicyManager::CanHandle | This telephony service can handle the requested number. |
QAbstractCallPolicyManager::MustHandle | This telephony service must handle the requested number. This is typically used for special emergency calls that must be placed over a GSM network even if an Asterisk network is also registered at the same time. This value overrides the CanHandle answers from all other call policy managers. |
QAbstractCallPolicyManager::NeverHandle | This telephony service would normally return MustHandle because it is supposed to handle the call, but the call cannot be placed right now. This is typically used for emergency numbers when the phone device is in flight mode. This value causes Qt Extended to reject the call immediately without attempting to place it. |
The following summarises the steps involved in integrating a third-party VoIP agent into Qt Extended:
The following are the most common problems that you are likely to encounter while integrating a third-party VoIP agent:
Service not started | You must add a service definition file to $QPEDIR/services/Telephony to tell the Qt Extended server how to launch your telephony service daemon. |
Service starts and then immediately exits | You must use QtopiaApplication::registerRunningTask() to ensure that Qt Extended will keep the daemon running after the Telephony::start() QCop message is received. Upon shutdown, you can call QtopiaApplication::unregisterRunningTask() and your daemon will gracefully exit. |
Service not registered | The service will not be properly registered if you do not call the initialize() function on your QTelephonyService instance. You can detect if this is the case by using vsexplorer to inspect the value space underneath /Communications/QNetworkRegistration/asterisk where asterisk is replaced with the name of your service. There should be entries for requestChannel and responseChannel if the service is properly registered. |
Call types are not registered | It will not be possible to place phone calls if the call types have not been properly registered with setCallTypes(). You can detect this by using vsexplorer to inspect the value space underneath /Communications/QPhoneCallProvider/CallTypes. There should be an entry for your service name (asterisk in our example} listing the supported call types. |
Incoming calls are not announced | Make sure that you have created the QPhoneCallImpl instance correctly, and have emitted a state change using QPhoneCallProvider::beginStateTransaction() and QPhoneCallProvider::endStateTransaction(). The same applies when calls change state. If the state transaction is not sent, the rest of Qt Extended will be unaware that the call has changed state. |
Call policy manager not registered | Make sure that QTOPIA_TASK_INTERFACE(AsteriskManager) appears in the .h file and that QTOPIA_TASK(Asterisk, AsteriskManager), QTOPIA_TASK_PROVIDES(Asterisk, AsteriskManager), and QTOPIA_TASK_PROVIDES(Asterisk, QAbstractCallPolicyManager) appear in the .cpp file, where Asterisk is replaced with the name of your telephony service. |
Copyright © 2009 Nokia | Trademarks | Qt Extended 4.4.3 |