--- a/qthighway/xqservice/src/xqserviceprovider.cpp Tue Aug 31 16:02:37 2010 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,758 +0,0 @@
-/*
-* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
-* All rights reserved.
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Lesser General Public License as published by
-* the Free Software Foundation, version 2.1 of the License.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public License
-* along with this program. If not,
-* see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/".
-*
-* Description:
-*
-*/
-
-#include "xqservicelog.h"
-
-#include <xqserviceprovider.h>
-#include <qmetaobject.h>
-#include <QByteArray>
-
-#include <xqserviceadaptor.h>
-//#include <xqserviceservice.h>
-#include <xqserviceutil.h>
-
-/*!
- \class ServiceAdaptorProxy
- \brief Proxy class for converting signal and slot members into IPC message names
-*/
-class ServiceAdaptorProxy : public XQServiceAdaptor
-{
- Q_OBJECT
-
-public:
- ServiceAdaptorProxy(const QString &channel, QObject *parent=0);
- virtual ~ServiceAdaptorProxy() ;
-
- QString memberToMessage( const QByteArray& member );
-};
-
-ServiceAdaptorProxy::ServiceAdaptorProxy(const QString &channel, QObject *parent) :
- XQServiceAdaptor(channel, parent)
-{
- XQSERVICE_DEBUG_PRINT("ServiceAdaptorProxy::ServiceAdaptorProxy");
- XQSERVICE_DEBUG_PRINT("channel: %s", qPrintable(channel));
-}
-
-ServiceAdaptorProxy::~ServiceAdaptorProxy()
-{
- XQSERVICE_DEBUG_PRINT("ServiceAdaptorProxy::~ServiceAdaptorProxy");
-}
-
-QString ServiceAdaptorProxy::memberToMessage( const QByteArray& member )
-{
- XQSERVICE_DEBUG_PRINT("ServiceAdaptorProxy::memberToMessage");
- XQSERVICE_DEBUG_PRINT("member: %s", member.constData());
-// TO BE CHECKED
-// return m_channel + "::" + XQServiceAdaptor::memberToMessage( member );
- return XQServiceAdaptor::memberToMessage( member );
-}
-
-/*!
- \class XQServiceProvider_Private
- \inpublicgroup QtBaseModule
-
- \brief Private implementation of XQServiceProvider
-*/
-class XQServiceProvider_Private
-{
-public:
- XQServiceProvider_Private(const QString &service);
-
- ~XQServiceProvider_Private();
-
- XQServiceAdaptor *m_adaptor;
-
- QString m_service;
- bool m_publishAllCalled;
- QObject* plugin;
-};
-
-XQServiceProvider_Private::XQServiceProvider_Private(const QString &service) :
- m_adaptor(NULL),
- m_service(service),
- m_publishAllCalled(false),
- plugin(NULL)
-{
- XQSERVICE_DEBUG_PRINT("XQServiceProvider_Private::XQServiceProvider_Private");
- XQSERVICE_DEBUG_PRINT("service: %s", qPrintable(service));
- m_adaptor = new ServiceAdaptorProxy(service);
-}
-
-XQServiceProvider_Private::~XQServiceProvider_Private()
-{
- XQSERVICE_DEBUG_PRINT("XQServiceProvider_Private::~XQServiceProvider_Private");
- delete m_adaptor;
-}
-
-/*!
- \class XQServiceProvider
- \inpublicgroup QtBaseModule
-
- \brief The XQServiceProvider class provides an interface to messages on a XQService service
- which simplifies remote slot invocations
-
- Service messages consist of a service name, a message name, and a list of parameter values.
- Qt extension dispatches service messages to the applications associated with the service
- name, on the application's \c{QPE/Application/appname} channel, where
- \c{appname} is the application's name.
-
- <b>Service registration</b> \n
- Service provider need to register it's service into the system before they can be used by
- the service client. Registration is done by creating a XML formatted service configuration
- file and defining the service in the provider's .pro-file. QMake will notice service provider
- from the .pro-file, with help of the service.prf file, and generate a make file that uses
- a helper application xqsreg.exe. The helper application sqsreg.exe will generate an application
- registration resource file ( _reg.rss) from the configuration-file and provider's definitions
- that include the needed declarations for the services provided.
-
- <b>Service Names Allocation</b> \n
- The harmonize service and interface names the Symba specific names and guidelines can be found
- from http://s60wiki.nokia.com/S60Wiki/QtFw_for_S60_coding_conventions/Service_name_registry#Service.
-
- Before implementing a service you need to allocate the name according to the guidelines. Please
- inform intended service clients (customers) and matti.parnanen@nokia.com.
-
- <b>Service Configuration File</b> \n
- All the service configuration are added to the run-time service registry to make them available
- for service discovery and creating service requests.
- \note Only one service element with multiple interface is supported!
-
- To support services a new configuration was introduced to qmake and two new variables for
- that configuration:
-
- \code
- CONFIG = service
- service.file = <service configuration file path>
- service.options = <embeddable> (optional, default not embeddable), <hidden> (optional, default not hidden)
- \endcode
-
- The fornat of the service configuration file is same as XML format used in Qt Service Framework.
- Example configuration file:
-
- \code
- <?xml version="1.0" encoding="utf-8" ?>
- <service>
- <name>Music Fetcher</name>
- <filepath>No path</filepath>
- <description>Music Fetcher</description>
- <interface>
- <name><b>com.nokia.symbian.IMusicFetch</b></name>
- <version>1.0</version>
- <description>Interface for fetching music files</description>
- </interface>
- </service>
- \endcode
-
- \code
- <ELEMENT service ( name, filepath, description?, interface+ ) >
- <ELEMENT description ( #CDATA ) >
- <ELEMENT filepath ( #PCDATA ) >
- <ELEMENT interface ( '''name''', version, description?, capabilities?, customproperty* ) >
- <ELEMENT capabilities ( #PCDATA ) >
- <ELEMENT name ( #PCDATA ) >
- <ELEMENT version ( #PCDATA ) >
- <ELEMENT customproperty ( #CDATA ) >
- <ATTLIST customproperty key NMTOKEN #REQUIRED >
- \endcode
-
- Also the old format described below is supported, With old format you can not have custom properties, which
- for example are used for AIW purposes.
- \code
- <ELEMENT service ( description?, interface+ ) >
- <ATTLIST service name #CDATA #REQUIRED >
- <ATTLIST service filepath #CDATA #REQUIRED >
- <ELEMENT description ( #CDATA ) >
- <ELEMENT interface ( description? ) >
- <ATTLIST interface '''name''' #CDATA #REQUIRED >
- <ATTLIST interface version #CDATA #REQUIRED >
- <ATTLIST interface capabilities #CDATA #REQUIRED >
- \endcode
-
- <b>Changing service or interface names</b> \n
- Before you think about changing the name of the already released and used service implementation, read this
- http://s60wiki.nokia.com/S60Wiki/QtFw_for_S60_coding_conventions/Service_name_registry#About_changing_service_or_interface_names
- first.
-
- The basic message is the service name, interface name and operation (message) slot signatures for the API. And for API changes you have to apply development time API deprecation process.
-
- <b>Service Registration tools</b> \n
- The needed utility files for service registration:
- - xqsreg.exe should be in \epoc32\tools or some other directory that can be found from the path
- - service.prf should be in \epoc32\tools\qt\mkspecs\features\symbian directory.
-
- If necessary you can copy those files to target directories from qthighway/bin.
-
- Sources for the xqsreg.exe can be found from the qthighway\xqsreg and it is also possible to compile it.
- - cd \qthighway\xqsreg
- - qmake -platform win32-mwc
- - make
-
- Usage: \n
- How to create a simple synchronously working service provider?
- \code
- class YourService : public XQServiceProvider
- {
- Q_OBJECT
-
- public:
- YourService ( ServiceApp *parent = 0 );
- ~YourService ();
-
- public slots:
- void functionName1();
- int functionName2(const QString& number, int times);
-
- private:
- ServiceApp *mServiceApp;
- };
- \endcode
-
- Implementation:
- \code
- YourService::YourService(ServiceApp* parent)
- : XQServiceProvider(QLatin1String("yourservice.Interface"), parent), mServiceApp(parent)
- {
- publishAll();
- }
-
- YourService::~YourService() { }
-
- void YourService::functionName1() { }
-
- int YourService::functionName2(const QString& number, int times)
- {
- int returnValue = 1;
- return returnValue;
- }
- \endcode
-
- Additions to .pro-file:
- \code
- CONFIG += service
- SERVICE.FILE = service_conf.xml
- SERVICE.OPTIONS = embeddable
- SERVICE.OPTIONS += hidden
- \endcode
-
- Service configuration file (service_conf.xml):
- \code
- <?xml version="1.0" encoding="utf-8" ?>
- <service>
- <name>yourservice</name>
- <filepath>No path</filepath>
- <description>Service description</description>
- <interface>
- <name>Interface</name>
- <version>1.0</version>
- <description>Interface description</description>
- </interface>
- </service>
- \endcode
-
- How to create a simple asynchronously working service provider?
- Header:
- \code
- class YourService : public XQServiceProvider
- {
-
- Q_OBJECT
-
- public:
- YourService ( ServiceApp *parent = 0 );
- ~YourService ();
- void compleAsyncFunction();
-
- public slots:
- void functionName1();
- int functionName2(const QString& number, int times);
-
- private:
- ServiceApp *mServiceApp;
- int mAsyncRequestIndex;
- QVariant mReturnValue;
-
- };
- \endcode
-
- Implementation:
- \code
- YourService::YourService(ServiceApp* parent)
- : XQServiceProvider(QLatin1String("yourservice.Interface"), parent), mServiceApp(parent)
- {
- publishAll();
- }
-
- YourService::~YourService() { }
-
- void YourService::compleAsyncFunction()
- {
- completeRequest(mAsyncRequestIndex, mReturnValue);
- }
-
- void YourService::functionName1()
- {
- mAsyncRequestIndex = setCurrentRequestAsync();
- mReturnValue.setValue(0);
- }
-
- int YourService::functionName2(const QString& number, int times)
- {
- mAsyncRequestIndex = setCurrentRequestAsync();
- mReturnValue.setValue(1);
- return mReturnValue.toInt();
- }
- \endcode
-
- <b>Examples:</b> \n
- The use of XQServiceProvider will be demonstrated using the \c{Time}
- service. This has a single message called \c{editTime()} which asks
- the service to pop up a dialog allowing the user to edit the current time.
- \code
- class TimeService : public XQServiceProvider
- {
- Q_OBJECT
- public:
- TimeService( QObject *parent = 0 );
-
- public slots:
- void editTime(QTime time);
- };
-
- TimeService::TimeService( QObject *parent )
- : XQServiceProvider( "Time", parent )
- {
- publishAll();
- }
- \endcode
-
- The call to publishAll() causes all public slots within \c{TimeService}
- to be automatically registered as Service messages. This can be
- useful if the service has many message types.
-
- The client can send a request to the service using QtopiaServiceRequest:
-
- \code
- XQServiceRequest req( "Time", "editTime()" );
- req << QTime::currentTime();
- req.send();
- \endcode
-
- <b>URI viewer</b> \n
- This is a simple example for implementing out-of-process scheme handlers.
- - "http", "https" and are handled via standard QDesktopServices::openUrl() function.
- This is fire-and-forget launch. The options are ignored and no control and signals available after the launch.
- - "appto" is routed to Activity Manager for opening the attached activity.
- This is fire-and-forget launch. The options are ignored and no control and signals available after the launch.
- - The "file" scheme is handled as the QFile based create below.
- So the com.nokia.symbian.IFileView interface is applied as for the QFile.
-
- Service application needs to publish support for:
- - The common interface "com.nokia.symbian.IUriView", and
- - The scheme(s), like "testo" in the example below. The custom custom property "schemes" contains one or more schemes as comma separated list (CSV)
- - The slot "view(QString)" to view the URI
-
- \code
- <?xml version="1.0" encoding="utf-8" ?>
- <service>
- <name>serviceapp</name>
- <filepath>No path</filepath>
- <description>Test service</description>
- <interface>
- <name>com.nokia.symbian.IUriView</name>
- <version>1.0</version>
- <description>Interface for showing URIs</description>
- <customproperty key="schemes">testto</customproperty>
- </interface>
- </service>
- \endcode
-
- An service application that offers support for a scheme implements the common "UriService" with the pre-defined "view" slot:
-
- \code
- class UriService : public XQServiceProvider
- {
- Q_OBJECT
- public:
- UriService( ServiceApp *parent = 0 );
- ~UriService();
- bool asyncAnswer() {return mAsyncAnswer;}
- void complete(bool ok);
-
- public slots:
- bool view(const QString& uri);
-
- private slots:
- void handleClientDisconnect();
-
- private:
- ServiceApp* mServiceApp;
- bool mAsyncAnswer;
- int mAsyncReqId;
- bool mRetValue;
- };
- \endcode
-
- Client application accesses the service via the URI:
-
- \code
- // Assume in example we have own scheme "testo" but this can be applied to
- // "mailto", etc. standard schemes.
- //
- // (As mentioned in the documentation, some schemes are CURRENTLY handled specially,
- // like "http" scheme uses QDesktopServices::openUrl).
- //
- QUrl url("testto://authority?param1=value1¶m1=value2");
-
- // The difference to the previous example is is how request is created
- // via application mgr.
-
- request = mAiwMgr.create(url);
- if (request == NULL)
- {
- // No handlers for the URI
- return;
- }
-
- // Set function parameters
- QList<QVariant> args;
- args << uri.toSring();
- request->setArguments(args);
-
- // Send the request
- bool res = request.send();
- if (!res)
- {
- // Request failed.
- int error = request->lastError();
- // Handle error
- }
-
- // If making multiple requests to same service, you can save the request as member variable
- // In this example all done.
- delete request;
- \endcode
-
- <b>File viewer</b> \n
- As for URis, a service application that support viewing a file with a dedicated MIME-type need to publish support for:
- - The common interface "com.nokia.symbian.IFileView".
- - The slot "view(QString)" to view the non-data-caged file by file name.
- - The slot "view(XQSharable)" to view the data-caged file by sharable file handle.
- - MIME type list (registered in the .pro file).
- So there are multiple service applications implementing the same interface.
-
- In service provider side you need the following entry in XML:
-
- \code
- <interface>
- <name>com.nokia.symbian.IFileView</name>
- <version>1.0</version>
- <description>Interface for showing Files</description>
- </interface>
- \endcode
-
- The file viewer application shall offer slots both for viewing filename (QString) and viewing sharable file (XQSharable):
-
- \code
- class FileService : public XQServiceProvider
- {
- Q_OBJECT
- public:
- FileService( ServiceApp *parent = 0 );
- ~FileService();
- bool asyncAnswer() {return mAsyncAnswer;}
- void complete(bool ok);
-
- public slots:
- bool view(QString file);
- bool view(XQSharableFile file);
-
- private slots:
- void handleClientDisconnect();
-
- private:
- ServiceApp* mServiceApp;
- bool mAsyncAnswer;
- int mAsyncReqId;
- bool mRetValue;
- };
- \endcode
-
- In the .pro file the service publishes the supported MIME types, e.g:
-
- \code
- RSS_RULES += \
- "datatype_list = " \
- " {" \
- " DATATYPE" \
- " {" \
- " priority = EDataTypePriorityNormal;" \
- " type = \"text/plain\";" \
- " }" \
- " };" \
- \endcode
-
- In the client side (see the "examples/appmgrclient" and "examples/serviceapp" included in the QtHighway release) access to
- file:
-
- \code
- // Not data caged file
- QFile file("C:\\data\\Others\\test.txt");
-
- request = mAiwMgr.create(file);
- if (request == NULL)
- {
- // No handlers for the URI
- return;
- }
- // By default operation is "view(QString)"
-
- // Set function parameters
- QList<QVariant> args;
- args << file.fileName();
- request->setArguments(args);
-
- // Send the request
- bool res = request.send();
- if (!res)
- {
- // Request failed.
- int error = request->lastError();
-
- // Handle error
- }
-
- // If making multiple requests to same service, you can save the request as member variable
- // In this example all done.
- delete request;
- \endcode
-
- <b>Sharable file viewer</b> \n
- The same rules as for file name based view applies, but different argument type (XQSharableFile) used
- in request. See the "examples/appmgrclient" and "examples/serviceapp" included in the QtHighway release.
-
- \code
- XQSharableFile sf;
- // Open the file for sharing from own private directory
- // If you have handle available, just set it by "setHandle()" function
- if (!sf.open("c:\\private\\e0022e74\\test.txt"))
- {
- // Failed to open sharable file
- return;
- }
-
- // Create request for the sharable file
- XQAiwreqiuest req = mAiwMgr.create(sf);
- if (!req)
- {
- // No viewer app found for the file
- // As we opened the handle, we need to close it !
- sf.close();
- return;
- }
- // By default operation is "view(XQSharableFile)"
-
- // Set function parameters
- // Not only one sharable handle supported, otherwise upon send EArgumentError error occurs
- QList<QVariant> args;
- args << qVariantFromValue(sf);
- req->setArguments(args);
-
- // Send the request
- bool res = request.send();
- if (!res)
- {
- // Request failed.
- int error = request->lastError();
- // Handle error
- }
-
- // As we opened the handle, we need to close it !
- sf.close();
-
- // If making multiple requests to same service, you can save the request as member variable
- // In this example all done.
- delete request;
- \endcode
-
- <b> Create interface action </b> \n
- One interface XML may offer one action to be displayed by client application.
- See the "examples/appmgrclient" and "examples/hbserviceprovider" included in the QtHighway release.
-
- \code
- HbAction* ShareUiPrivate::fetchServiceAction(XQAiwInterfaceDescriptor interfaceDescriptor)
- {
- QDEBUG_WRITE("ShareUiPrivate::fetchServiceAction start");
- // create the request for each descriptor.
-
- XQAiwRequest* request = mAppManager.create(interfaceDescriptor,SELECT_OP,false);
- QAction action = request->createAction());
- if (!action)
- return 0;
-
- // if Orbit widgets do not support QAction
- // Need to convert QAction to HbAction first
- HbAction* hbAction = convertAction(action);
- if(hbAction)
- {
- // Connect triggered signals to enable the request to emit triggered
- connect(hbAction, SIGNAL(triggered()), action, SIGNAL(triggered()));
-
- // connect the request's triggered action to the slot in app
- connect(request, SIGNAL(triggered()), this, SLOT(onTriggered()));
- }
-
- return hbAction;
- }
- \endcode
-
- In service provider side you need to have the following entries in XML to be converted to QAction by the create:
-
- \code
- <interface>
- <name>Dialer></name>
- <version=1.0</version>
- <description>Dial interface</description>
- <customproperty key="aiw_action_text_file">hbserviceprovider</customproperty>
- <customproperty key="aiw_action_text">txt_aiw_action_text</customproperty>
- </interface>
- \endcode
-*/
-
-/*!
- \fn void XQServiceProvider::returnValueDelivered()
-
- This signal is emitted when asynchronous request has been completed and its
- return value has been delivered to the service client.
-*/
-
-/*!
- \fn void XQServiceProvider::clientDisconnected()
-
- This signal is emitted if client accessing a service application terminates.
- The counterpart in client side (when service application terminates) is
- the error XQService::EConnectionClosed.
-*/
-
-/*!
- Construct a remote service object for \a service and attach it to \a parent.
- \param service Defines the full service name that is implemented.
- The full service name is:
- - The name of the service from the service configuration file
- - Character *.* (dot)
- - The name of the interface from the service configuration file
- \param parent Parent of this QObject
-*/
-XQServiceProvider::XQServiceProvider( const QString& service, QObject *parent )
- : QObject( parent )
-{
- XQSERVICE_DEBUG_PRINT("XQServiceProvider::XQServiceProvider");
- XQSERVICE_DEBUG_PRINT("service: %s", qPrintable(service));
- m_data = new XQServiceProvider_Private(service);
- connect(m_data->m_adaptor, SIGNAL(returnValueDelivered()), this, SIGNAL(returnValueDelivered()));
- connect(m_data->m_adaptor, SIGNAL(clientDisconnected()), this, SIGNAL(clientDisconnected()));
-}
-
-/*!
- Destroys this service handling object.
-*/
-XQServiceProvider::~XQServiceProvider()
-{
- XQSERVICE_DEBUG_PRINT("XQServiceProvider::~XQServiceProvider");
- if (m_data)
- delete m_data;
-}
-
-
-void XQServiceProvider::SetPlugin(QObject* impl_plugin)
- {
- m_data->plugin=impl_plugin;
- }
-
-
-/*!
- Publishes all slots on this object within subclasses of XQServiceProvider.
- This is typically called from a subclass constructor.
-*/
-void XQServiceProvider::publishAll()
-{
- XQSERVICE_DEBUG_PRINT("XQServiceProvider::publishAll");
- if (!m_data->plugin) {
- m_data->m_adaptor->publishAll(this,XQServiceProvider::staticMetaObject.methodCount(),XQServiceAdaptor::Slots);
- }
- else {
- m_data->m_adaptor->publishAll(m_data->plugin, 0, XQServiceAdaptor::Slots);
- }
-}
-
-/*!
- Sets current request to asynchronous mode so that provider can complete the
- request later via the completeRequest() call.
- \return Request ID which shall be used in the completeRequest() call.
- \note There can be several clients accessing the same service at the same time. Avoid saving
- the index to XQServiceProvider instance as member variable as when another new request
- comes in, it will have different index and you will potentially override the index of
- the first request. You should ensure the completeRequest() gets the correct index e.g.
- by attaching the index as user data to data object maintain a map of indexes based on
- some key.
-*/
-int XQServiceProvider::setCurrentRequestAsync()
-{
- XQSERVICE_DEBUG_PRINT("XQServiceProvider::setCurrentRequestAsync");
- return m_data->m_adaptor->setCurrentRequestAsync();
-}
-
-/*!
- \fn bool XQServiceProvider::completeRequest(int index, const T& retValue)
-
- Completes asynchronous request.
- \param index Defines the index of the asynchronous request to complete.
- \param retValue defines the return value for the request.
- \return true if request could be completed successfully, otherwise false.
- \sa completeRequest()
-*/
-
-/*!
- Completes the asynchronous request with the given value
- \param index Request ID got from the setCurrentRequestAsync call.
- \param retValue Returned value.
- \return true on success, false if index points to non-existing request.
- \note <b>You need to check the return value. </b>
- If false it means connection to client has been lost and the complete will not ever succeed.
- So if you have e.g. a code that quits application using the ReturnValueDelived signal only,
- that signal will never be emitted as request can not be completed.
-*/
-bool XQServiceProvider::completeRequest(int index, const QVariant& retValue)
-{
- XQSERVICE_DEBUG_PRINT("XQServiceProvider::completeRequest");
- XQSERVICE_DEBUG_PRINT("index: %d, retValue: %s", index, qPrintable(retValue.toString()));
- return m_data->m_adaptor->completeRequest(index, retValue);
-}
-
-/*!
- Return additional request information attached to request
- \return Request info.
-*/
-XQRequestInfo XQServiceProvider::requestInfo() const
-{
- return m_data->m_adaptor->requestInfo();
-}
-
-#include "xqserviceprovider.moc"