diff options
author | Alex <qt-info@nokia.com> | 2011-06-15 19:14:16 +1000 |
---|---|---|
committer | Alex <qt-info@nokia.com> | 2011-06-17 17:31:52 +1000 |
commit | 0e3be465c935700019f6232f88381613d51776d7 (patch) | |
tree | e2a44eda69ebd38183ea251532e0da9fcb6020a6 /src | |
parent | 79ec22e9cd8ef8f679c541c4fd76d616e737b8bf (diff) |
Add first version of QtServiceFramework library
Diffstat (limited to 'src')
71 files changed, 20052 insertions, 1 deletions
diff --git a/src/serviceframework/databasemanager.cpp b/src/serviceframework/databasemanager.cpp new file mode 100644 index 00000000..92ad8476 --- /dev/null +++ b/src/serviceframework/databasemanager.cpp @@ -0,0 +1,914 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "databasemanager_p.h" +#include "qserviceinterfacedescriptor_p.h" +#include <QFileSystemWatcher> +#include <QHash> + +QTM_BEGIN_NAMESPACE + +DatabaseFileWatcher::DatabaseFileWatcher(DatabaseManager *parent) + : QObject(parent), + m_manager(parent), + m_watcher(0) +{ +} + +QString DatabaseFileWatcher::closestExistingParent(const QString &path) +{ + if (QFile::exists(path)) + return path; + + int lastSep = path.lastIndexOf(QDir::separator()); + if (lastSep < 0) + return QString(); + return closestExistingParent(path.mid(0, lastSep)); +} + +void DatabaseFileWatcher::restartDirMonitoring(const QString &dbPath, const QString &previousDirPath) +{ + if (m_watcher->files().contains(dbPath)) + return; + + QString existing = closestExistingParent(dbPath); + if (existing.isEmpty()) { + qWarning() << "QServiceManager: can't find existing directory for path to database" << dbPath + << "serviceAdded() and serviceRemoved() will not be emitted"; + return; + } + if (existing == dbPath) { + ServiceDatabase *db = 0; + DatabaseManager::DbScope scope; + if (m_manager->m_userDb && dbPath == m_manager->m_userDb->databasePath()) { + db = m_manager->m_userDb; + scope = DatabaseManager::UserOnlyScope; + } else if (dbPath == m_manager->m_systemDb->databasePath()) { + db = m_manager->m_systemDb; + scope = DatabaseManager::SystemScope; + } + + if (db) { + if (!previousDirPath.isEmpty()) + m_watcher->removePath(previousDirPath); + QMutableListIterator<QString> i(m_monitoredDbPaths); + while (i.hasNext()) { + if (i.next() == dbPath) + i.remove(); + } + + QStringList newServices = m_manager->getServiceNames(QString(), scope); + for (int i=0; i<newServices.count(); i++) + emit m_manager->serviceAdded(newServices[i], scope); + setEnabled(db, true); + } + } else { + if (previousDirPath != existing) { + if (!previousDirPath.isEmpty()) + m_watcher->removePath(previousDirPath); + if (!m_watcher->directories().contains(existing)) + m_watcher->addPath(existing); + if (!m_monitoredDbPaths.contains(dbPath)) + m_monitoredDbPaths << dbPath; + } + } +} + +void DatabaseFileWatcher::setEnabled(ServiceDatabase *database, bool enabled) +{ + if (!m_watcher) { + m_watcher = new QFileSystemWatcher(this); + connect(m_watcher, SIGNAL(fileChanged(QString)), + SLOT(databaseChanged(QString))); + connect(m_watcher, SIGNAL(directoryChanged(QString)), + SLOT(databaseDirectoryChanged(QString))); + } + + QString path = database->databasePath(); + if (enabled) { + if (QFile::exists(path)) { + m_knownServices[path] = database->getServiceNames(QString()); + m_watcher->addPath(path); + } else { + restartDirMonitoring(path, QString()); + } + } else { + m_watcher->removePath(path); + m_knownServices.remove(path); + } +} + +void DatabaseFileWatcher::databaseDirectoryChanged(const QString &path) +{ + for (int i=0; i<m_monitoredDbPaths.count(); i++) { + if (m_monitoredDbPaths[i].contains(path)) + restartDirMonitoring(m_monitoredDbPaths[i], path); + } +} + +void DatabaseFileWatcher::databaseChanged(const QString &path) +{ + if (m_manager->m_userDb && path == m_manager->m_userDb->databasePath()) + notifyChanges(m_manager->m_userDb, DatabaseManager::UserScope); + else if (path == m_manager->m_systemDb->databasePath()) + notifyChanges(m_manager->m_systemDb, DatabaseManager::SystemScope); + + // if database was deleted, the path may have been dropped + if (!m_watcher->files().contains(path) && QFile::exists(path)) + m_watcher->addPath(path); +} + +void DatabaseFileWatcher::notifyChanges(ServiceDatabase *database, DatabaseManager::DbScope scope) +{ + #ifdef Q_OS_SYMBIAN + scope = DatabaseManager::SystemScope; + #endif + + QString dbPath = database->databasePath(); + if (!QFile::exists(dbPath)) { + m_knownServices.remove(dbPath); + restartDirMonitoring(dbPath, QString()); + return; + } + + QStringList currentServices = database->getServiceNames(QString()); + if (database->lastError().code() !=DBError::NoError) { + qWarning("QServiceManager: failed to get current service names for serviceAdded() and serviceRemoved() signals"); + return; + } + + const QStringList &knownServicesRef = m_knownServices[dbPath]; + + QSet<QString> currentServicesSet = currentServices.toSet(); + QSet<QString> knownServicesSet = knownServicesRef.toSet(); + if (currentServicesSet == knownServicesSet) + return; + + QStringList newServices; + for (int i=0; i<currentServices.count(); i++) { + if (!knownServicesSet.contains(currentServices[i])) + newServices << currentServices[i]; + } + + QStringList removedServices; + for (int i=0; i<knownServicesRef.count(); i++) { + if (!currentServicesSet.contains(knownServicesRef[i])) + removedServices << knownServicesRef[i]; + } + + m_knownServices[dbPath] = currentServices; + for (int i=0; i<newServices.count(); i++) + emit m_manager->serviceAdded(newServices[i], scope); + for (int i=0; i<removedServices.count(); i++) + emit m_manager->serviceRemoved(removedServices[i], scope); +} + +bool lessThan(const QServiceInterfaceDescriptor &d1, + const QServiceInterfaceDescriptor &d2) +{ + return (d1.majorVersion() < d2.majorVersion()) + || ( d1.majorVersion() == d2.majorVersion() + && d1.minorVersion() < d2.minorVersion()); +} + +/* + \class DatabaseManager + The database manager is responsible for receiving queries about + services and managing user and system scope databases in order to + respond to those queries. + + It provides operations for + - registering and unregistering services + - querying for services and interfaces + - setting and getting default interface implementations + + and provides notifications by emitting signals for added + or removed services. + + Implementation note: + When one of the above operations is first invoked a connection with the + appropriate database(s) is opened. This connection remains + open until the DatabaseManager is destroyed. + + If the system scope database cannot be opened when performing + user scope operations. The operations are carried out as per normal + but only acting on the user scope database. Each operation invokation + will try to open a connection with the system scope database. + + Terminology note: + When referring to user scope regarding operations, it generally + means access to both the user and system databases with the + data from both combined into a single dataset. + When referring to a user scope database it means the + user database only. + + \since 1.0 +*/ + +/* + Constructor +*/ +DatabaseManager::DatabaseManager() + : m_userDb(NULL), + m_systemDb(new ServiceDatabase), + m_fileWatcher(0), + m_hasAccessedUserDb(false), + m_alreadyWarnedOpenError(false) +{ + #ifndef Q_OS_SYMBIAN + m_userDb = new ServiceDatabase; + initDbPath(UserScope); + #endif + initDbPath(SystemScope); +} + +/* + Destructor +*/ +DatabaseManager::~DatabaseManager() +{ + delete m_fileWatcher; + m_fileWatcher = 0; + + //Aside: databases are implicitly closed + //during deletion + if (m_userDb) { + m_userDb->close(); + delete m_userDb; + } + m_userDb = 0; + + if (m_systemDb) { + m_systemDb->close(); + delete m_systemDb; + } + m_systemDb = 0; +} + + +/* + Initialises database path of m_userDb + or m_systemDb, but does not open any + database connections +*/ +void DatabaseManager::initDbPath(DbScope scope) +{ + #ifdef Q_OS_SYMBIAN + scope = SystemScope; + #endif + + QSettings::Scope settingsScope; + QString dbIdentifier; + ServiceDatabase *db; + if (scope == SystemScope) { + settingsScope = QSettings::SystemScope; + dbIdentifier = QLatin1String("_system"); + db = m_systemDb; + } else { + settingsScope = QSettings::UserScope; + dbIdentifier = QLatin1String("_user"); + db = m_userDb; + } + +#ifdef QT_SIMULATOR + dbIdentifier.append(QLatin1String("_simulator")); +#endif + + #ifdef Q_OS_SYMBIAN + QDir dir(QDir::toNativeSeparators("C:\\Data\\temp\\QtServiceFW")); + #else + QSettings settings(QSettings::IniFormat, settingsScope, + QLatin1String("Nokia"), QLatin1String("QtServiceFramework")); + QFileInfo fi(settings.fileName()); + QDir dir = fi.dir(); + #endif + QString qtVersion = QLatin1String(qVersion()); + qtVersion = qtVersion.left(qtVersion.size() -2); //strip off patch version + QString dbName = QString(QLatin1String("QtServiceFramework_")) + qtVersion + dbIdentifier + QLatin1String(".db"); + db->setDatabasePath(dir.path() + QDir::separator() + dbName); +} + +/* + Adds the details \a service into the service database corresponding to + \a scope. + + Returns true if the operation succeeded and false otherwise. + The last error is set when this function is called. +*/ +bool DatabaseManager::registerService(ServiceMetaDataResults &service, DbScope scope) +{ + #ifdef Q_OS_SYMBIAN + scope = SystemScope; + #endif + + if (scope == DatabaseManager::SystemScope) { + if (!openDb(DatabaseManager::SystemScope)) { + return false; + } else { + if (!m_systemDb->registerService(service)) { + m_lastError = m_systemDb->lastError(); + return false; + } else { //must be successful registration + m_lastError.setError(DBError::NoError); + return true; + } + } + } else { //must be registering service at user scope + if (!openDb(DatabaseManager::UserScope)) { + return false; + } else { + if (!m_userDb->registerService(service)) { + m_lastError = m_userDb->lastError(); + return false; + } else { //must be successful registration + m_lastError.setError(DBError::NoError); + return true; + } + } + } +} + +/* + Removes the details of \serviceName from the database corresponding to \a + scope. + + Returns true if the operation succeeded, false otherwise. + The last error is set when this function is called. +*/ +bool DatabaseManager::unregisterService(const QString &serviceName, DbScope scope) +{ + #ifdef Q_OS_SYMBIAN + scope = SystemScope; + #endif + + if (scope == DatabaseManager::SystemScope) { + if (!openDb(DatabaseManager::SystemScope)) + return false; + else { + if (!m_systemDb->unregisterService(serviceName)) { + m_lastError = m_systemDb->lastError(); + return false; + } else { //must be successful unregistration + m_lastError.setError(DBError::NoError); + return true; + } + } + } else { + if (!openDb(DatabaseManager::UserScope)) { + return false; + } else { + if (!m_userDb->unregisterService(serviceName)){ + m_lastError = m_userDb->lastError(); + return false; + } else { //must be successful unregistration + m_lastError.setError(DBError::NoError); + return true; + } + } + } +} + +/* + Removes the initialization specific information of \serviceName from the database + corresponding to a \scope. + + Returns true if teh operation succeeded, false otherwise. + The last error is set when this function is called. + */ +bool DatabaseManager::serviceInitialized(const QString &serviceName, DbScope scope) +{ + ServiceDatabase *db = (scope == DatabaseManager::SystemScope) ? m_systemDb : m_userDb; + + if (!openDb(scope)) { + return false; + } else { + if (!db->serviceInitialized(serviceName)) { + m_lastError = db->lastError(); + return false; + } else { + m_lastError.setError(DBError::NoError); + return true; + } + } +} + +/* + Retrieves a list of interface descriptors that fulfill the constraints specified + by \a filter at a given \a scope. + + The last error is set when this function is called. +*/ +QList<QServiceInterfaceDescriptor> DatabaseManager::getInterfaces(const QServiceFilter &filter, DbScope scope) +{ + #ifdef Q_OS_SYMBIAN + QService::Scope requestedScope; + if (scope == UserScope) { + requestedScope = QService::UserScope; + } else { + requestedScope = QService::SystemScope; + } + scope = SystemScope; + #endif + + QList<QServiceInterfaceDescriptor> descriptors; + + int userDescriptorCount = 0; + if (scope == UserScope) { + if (!openDb(UserScope)) + return descriptors; + + descriptors = m_userDb->getInterfaces(filter); + if (m_userDb->lastError().code() != DBError::NoError ) { + descriptors.clear(); + m_lastError = m_userDb->lastError(); + return descriptors; + } + + userDescriptorCount = descriptors.count(); + for (int i=0; i < userDescriptorCount; ++i) { + descriptors[i].d->scope = QService::UserScope; + } + } + + if (openDb(SystemScope)) { + descriptors.append(m_systemDb->getInterfaces(filter)); + if (m_systemDb->lastError().code() != DBError::NoError) { + descriptors.clear(); + m_lastError = m_systemDb->lastError(); + return descriptors; + } + + for (int i = userDescriptorCount; i < descriptors.count(); ++i) { + #ifdef Q_OS_SYMBIAN + descriptors[i].d->scope = requestedScope; + #else + descriptors[i].d->scope = QService::SystemScope; + #endif + } + } else { + if ( scope == SystemScope) { + //openDb() should already have handled lastError + descriptors.clear(); + return descriptors; + } + } + + m_lastError.setError(DBError::NoError); + return descriptors; +} + + +/* + Retrieves a list of the names of services that provide the interface + specified by \a interfaceName. + + The last error is set when this function is called. +*/ +QStringList DatabaseManager::getServiceNames(const QString &interfaceName, DatabaseManager::DbScope scope) +{ + #ifdef Q_OS_SYMBIAN + scope = SystemScope; + #endif + + QStringList serviceNames; + if (scope == UserScope || scope == UserOnlyScope) { + if (!openDb(DatabaseManager::UserScope)) + return serviceNames; + serviceNames = m_userDb->getServiceNames(interfaceName); + if (m_userDb->lastError().code() != DBError::NoError) { + serviceNames.clear(); + m_lastError = m_userDb->lastError(); + return serviceNames; + } + if (scope == UserOnlyScope) { + m_lastError.setError(DBError::NoError); + return serviceNames; + } + } + + if (openDb(DatabaseManager::SystemScope)) { + QStringList systemServiceNames; + systemServiceNames = m_systemDb->getServiceNames(interfaceName); + if (m_systemDb->lastError().code() != DBError::NoError) { + serviceNames.clear(); + m_lastError = m_systemDb->lastError(); + return serviceNames; + } + foreach (const QString &systemServiceName, systemServiceNames) { + if (!serviceNames.contains(systemServiceName, Qt::CaseInsensitive)) + serviceNames.append(systemServiceName); + } + + } else { + if ( scope == SystemScope) { + //openDb() should have already handled lastError + serviceNames.clear(); + return serviceNames; + } + } + + m_lastError.setError(DBError::NoError); + return serviceNames; +} + +/* + Returns the default interface implementation descriptor for a given + \a interfaceName and \a scope. + + The last error is set when this function is called. +*/ +QServiceInterfaceDescriptor DatabaseManager::interfaceDefault(const QString &interfaceName, DbScope scope) +{ + #ifdef Q_OS_SYMBIAN + QService::Scope requestedScope; + if (scope == UserScope) { + requestedScope = QService::UserScope; + } else { + requestedScope = QService::SystemScope; + } + scope = SystemScope; + #endif + + QServiceInterfaceDescriptor descriptor; + if (scope == UserScope) { + if (!openDb(UserScope)) + return QServiceInterfaceDescriptor(); + QString interfaceID; + descriptor = m_userDb->interfaceDefault(interfaceName, &interfaceID); + + if (m_userDb->lastError().code() == DBError::NoError) { + descriptor.d->scope = QService::UserScope; + return descriptor; + } else if (m_userDb->lastError().code() == DBError::ExternalIfaceIDFound) { + //default hasn't been found in user db, but we have found an ID + //that may refer to an interface implementation in the system db + if (!openDb(SystemScope)) { + QString errorText(QLatin1String("No default service found for interface: \"%1\"")); + m_lastError.setError(DBError::NotFound, errorText.arg(interfaceName)); + return QServiceInterfaceDescriptor(); + } + + descriptor = m_systemDb->getInterface(interfaceID); + //found the service from the system database + if (m_systemDb->lastError().code() == DBError::NoError) { + m_lastError.setError(DBError::NoError); + descriptor.d->scope = QService::SystemScope; + return descriptor; + } else if (m_systemDb->lastError().code() == DBError::NotFound) { + //service implementing interface doesn't exist in the system db + //so the user db must contain a stale entry so remove it + m_userDb->removeExternalDefaultServiceInterface(interfaceID); + + QList<QServiceInterfaceDescriptor> descriptors; + descriptors = getInterfaces(QServiceFilter(interfaceName), UserScope); + + //make the latest interface implementation the new + //default if there is one + if (descriptors.count() > 0 ) { + descriptor = latestDescriptor(descriptors); + setInterfaceDefault(descriptor, UserScope); + m_lastError.setError(DBError::NoError); + return descriptor; + } else { + QString errorText(QLatin1String("No default service found for interface: \"%1\"")); + m_lastError.setError(DBError::NotFound, errorText.arg(interfaceName)); + return QServiceInterfaceDescriptor(); + } + } else { + m_lastError.setError(DBError::NoError); + return QServiceInterfaceDescriptor(); + } + } else if (m_userDb->lastError().code() == DBError::NotFound) { + //do nothing, the search for a default in the system db continues + //further down + } else { //error occurred at user db level, so return + m_lastError = m_userDb->lastError(); + return QServiceInterfaceDescriptor(); + } + } + + //search at system scope because we haven't found a default at user scope + //or because we're specifically only querying at system scope + if (!openDb(SystemScope)) { + if (scope == SystemScope) { + m_lastError = m_systemDb->lastError(); + return QServiceInterfaceDescriptor(); + } else if (scope == UserScope && m_userDb && m_userDb->lastError().code() == DBError::NotFound) { + m_lastError = m_userDb->lastError(); + return QServiceInterfaceDescriptor(); + } + } else { + descriptor = m_systemDb->interfaceDefault(interfaceName); + if (m_systemDb->lastError().code() == DBError::NoError) { + #ifdef Q_OS_SYMBIAN + descriptor.d->scope = requestedScope; + #else + descriptor.d->scope = QService::SystemScope; + #endif + return descriptor; + } else if (m_systemDb->lastError().code() == DBError::NotFound) { + m_lastError = m_systemDb->lastError(); + return QServiceInterfaceDescriptor(); + } else { + m_lastError = m_systemDb->lastError(); + return QServiceInterfaceDescriptor(); + } + } + + //should not be possible to reach here + m_lastError.setError(DBError::UnknownError); + return QServiceInterfaceDescriptor(); +} + +/* + Sets the default interface implemenation for \a interfaceName to the matching + interface implementation provided by \a service. + + If \a service provides more than one interface implementation, the newest + version of the interface is set as the default. + + Returns true if the operation was succeeded, false otherwise + The last error is set when this function is called. +*/ +bool DatabaseManager::setInterfaceDefault(const QString &serviceName, const + QString &interfaceName, DbScope scope) +{ + QList<QServiceInterfaceDescriptor> descriptors; + QServiceFilter filter; + filter.setServiceName(serviceName); + filter.setInterface(interfaceName); + + descriptors = getInterfaces(filter, scope); + if (m_lastError.code() != DBError::NoError) + return false; + + if (descriptors.count() == 0) { + QString errorText(QLatin1String("No implementation for interface \"%1\" " + "found for service \"%2\"")); + m_lastError.setError(DBError::NotFound, + errorText.arg(interfaceName) + .arg(serviceName)); + return false; + } + + //find the descriptor with the latest version + int latestIndex = 0; + for (int i = 1; i < descriptors.count(); ++i) { + if (lessThan(descriptors[latestIndex], descriptors[i])) + latestIndex = i; + } + + return setInterfaceDefault(descriptors[latestIndex], scope); +} + +/* + Sets the interface implementation specified by \a descriptor to be the default + implementation for the particular interface specified in the descriptor. + + Returns true if the operation succeeded, false otherwise. + The last error is set when this function is called. +*/ +bool DatabaseManager::setInterfaceDefault(const QServiceInterfaceDescriptor &descriptor, DbScope scope) +{ + #ifdef Q_OS_SYMBIAN + Q_UNUSED(scope); + if (!openDb(SystemScope)) { + return false; + } else { + if (m_systemDb->setInterfaceDefault(descriptor)) { + m_lastError.setError(DBError::NoError); + return true; + } else { + m_lastError = m_systemDb->lastError(); + return false; + } + } + #else + if (scope == UserScope) { + if (!openDb(UserScope)) + return false; + if (descriptor.scope() == QService::UserScope) { //if a user scope descriptor, just set it in the user db + if (m_userDb->setInterfaceDefault(descriptor)) { + m_lastError.setError(DBError::NoError); + return true; + } else { + m_lastError = m_userDb->lastError(); + return false; + } + } else { //otherwise we need to get the interfaceID from the system db and set this + //as an external default interface ID in the user db + if (!openDb(SystemScope)) + return false; + + QString interfaceDescriptorID = m_systemDb->getInterfaceID(descriptor); + if (m_systemDb->lastError().code() == DBError::NoError) { + if (m_userDb->setInterfaceDefault(descriptor, interfaceDescriptorID)) { + m_lastError.setError(DBError::NoError); + return true; + } else { + m_lastError = m_userDb->lastError(); + return false; + } + } else { + m_lastError = m_systemDb->lastError(); + return false; + } + } + } else { //scope == SystemScope + if (descriptor.scope() == QService::UserScope) { + QString errorText(QLatin1String("Cannot set default service at system scope with a user scope " + "interface descriptor")); + m_lastError.setError(DBError::InvalidDescriptorScope, errorText); + return false; + } else { + if (!openDb(SystemScope)) { + return false; + } else { + if (m_systemDb->setInterfaceDefault(descriptor)) { + m_lastError.setError(DBError::NoError); + return true; + } else { + m_lastError = m_systemDb->lastError(); + return false; + } + } + } + } + #endif +} + +/* + Opens a database connection with the database at a specific \a scope. + + The last error is set when this function is called. +*/ +bool DatabaseManager::openDb(DbScope scope) +{ + #ifdef Q_OS_SYMBIAN + scope = SystemScope; + #endif + + if (scope == SystemScope && m_systemDb->isOpen() && !QFile::exists(m_systemDb->databasePath())) { + delete m_systemDb; + m_systemDb = new ServiceDatabase; + initDbPath(SystemScope); + m_alreadyWarnedOpenError = false; + } else if (scope != SystemScope && m_userDb->isOpen() && !QFile::exists(m_userDb->databasePath())) { + delete m_userDb; + m_userDb = new ServiceDatabase; + initDbPath(UserScope); + m_alreadyWarnedOpenError = false; + } + + ServiceDatabase *db; + if (scope == SystemScope) { + db = m_systemDb; + } + else { + db = m_userDb; + m_hasAccessedUserDb = true; + } + + if (db->isOpen()) + return true; + + bool isOpen = db->open(); + if (!isOpen) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "DatabaseManger::openDb():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + if (scope == SystemScope && m_hasAccessedUserDb == true) { + if (QFile::exists(m_systemDb->databasePath()) && !m_alreadyWarnedOpenError) + qWarning() << "Service Framework:- Unable to access system database for a user scope " + "operation; resorting to using only the user database. Future operations " + "will attempt to access the system database but no further warnings will be issued"; + } + + QString warning; + if (db->lastError().code() == DBError::InvalidDatabaseFile) { + warning = QString(QLatin1String("Service Framework:- Database file is corrupt or invalid: ")) + db->databasePath(); + m_lastError = db->lastError(); + } else { + warning = QString(QLatin1String("Service Framework:- Unable to open or create database at: ")) + db->databasePath(); + QString errorText(QLatin1String("Unable to open service framework database: %1")); + m_lastError.setError(DBError::CannotOpenServiceDb, + errorText.arg(db->databasePath())); + } + + if (m_alreadyWarnedOpenError + || (scope == SystemScope && m_hasAccessedUserDb && !QFile::exists(m_systemDb->databasePath()))) { + //do nothing, don't output warning if already warned or we're accessing the system database + //from user scope and the system database doesn't exist + } else { + qWarning() << qPrintable(warning); + m_alreadyWarnedOpenError = true; + } + + return false; + } + + //if we are opening the system database while the user database is open, + //cleanup and reset any old external defaults + //from the user scope database + if (scope == SystemScope && m_userDb && m_userDb->isOpen()) { + QList<QPair<QString,QString> > externalDefaultsInfo; + externalDefaultsInfo = m_userDb->externalDefaultsInfo(); + QServiceInterfaceDescriptor descriptor; + QPair<QString,QString> defaultInfo; + + for (int i = 0; i < externalDefaultsInfo.count(); ++i) { + defaultInfo = externalDefaultsInfo[i]; + descriptor = m_userDb->getInterface(defaultInfo.second); + if (m_userDb->lastError().code() == DBError::NotFound) { + m_userDb->removeExternalDefaultServiceInterface(defaultInfo.second); + QList<QServiceInterfaceDescriptor> descriptors; + descriptors = getInterfaces(QServiceFilter(defaultInfo.first), UserScope); + + if (descriptors.count() > 0 ) { + descriptor = latestDescriptor(descriptors); + setInterfaceDefault(descriptor, UserScope); + } + } + } + } + + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Returns the interface descriptor with the highest version from the + list of interface \a descriptors +*/ +QServiceInterfaceDescriptor DatabaseManager::latestDescriptor( + const QList<QServiceInterfaceDescriptor> &descriptors) +{ + if (descriptors.count() == 0) + return QServiceInterfaceDescriptor(); + + int latestIndex = 0; + for (int i = 1; i < descriptors.count(); ++i) { + if (lessThan(descriptors[latestIndex], descriptors[i])) + latestIndex = i; + } + + return descriptors[latestIndex]; +} + +/* + Sets whether change notifications for added and removed services are + \a enabled or not at a given \a scope. +*/ +void DatabaseManager::setChangeNotificationsEnabled(DbScope scope, bool enabled) +{ + #ifdef Q_OS_SYMBIAN + scope = SystemScope; + #endif + + if (!m_fileWatcher) m_fileWatcher = new + DatabaseFileWatcher(this); m_fileWatcher->setEnabled(scope == SystemScope ? + m_systemDb : m_userDb, enabled); +} + +#include "moc_databasemanager_p.cpp" + +QTM_END_NAMESPACE diff --git a/src/serviceframework/databasemanager_p.h b/src/serviceframework/databasemanager_p.h new file mode 100644 index 00000000..b5ff070f --- /dev/null +++ b/src/serviceframework/databasemanager_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DATABASEMANAGER_H_ +#define DATABASEMANAGER_H_ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qserviceframeworkglobal.h" +#include "servicedatabase_p.h" +#include <QObject> + +#ifdef QT_SFW_SERVICEDATABASE_GENERATE +#undef Q_AUTOTEST_EXPORT +#define Q_AUTOTEST_EXPORT +#endif + +QT_BEGIN_HEADER +QTM_BEGIN_NAMESPACE + +class DatabaseFileWatcher; +class Q_AUTOTEST_EXPORT DatabaseManager : public QObject +{ + Q_OBJECT + + public: + enum DbScope{UserScope, SystemScope, UserOnlyScope}; + DatabaseManager(void); + virtual ~DatabaseManager(); + + bool registerService(ServiceMetaDataResults &service, DbScope scope); + bool unregisterService(const QString &serviceName, DbScope scope); + bool serviceInitialized(const QString &serviceName, DbScope scope); + + QList<QServiceInterfaceDescriptor> getInterfaces(const QServiceFilter &filter, DbScope scope); + QStringList getServiceNames(const QString &interfaceName, DbScope scope); + + QServiceInterfaceDescriptor interfaceDefault(const QString &interfaceName, DbScope scope); + bool setInterfaceDefault(const QString &serviceName, const QString &interfaceName, DbScope scope); + bool setInterfaceDefault(const QServiceInterfaceDescriptor &interface, DbScope scope); + + DBError lastError(){ return m_lastError;} + + void setChangeNotificationsEnabled(DbScope scope, bool enabled); + + signals: + void serviceAdded(const QString &serviceName, DatabaseManager::DbScope scope); + void serviceRemoved(const QString &serviceName, DatabaseManager::DbScope scope); + + private: + void initDbPath(DbScope scope); + bool openDb(DbScope scope); + + ServiceDatabase *m_userDb; + ServiceDatabase *m_systemDb; + DBError m_lastError; + + friend class DatabaseFileWatcher; + DatabaseFileWatcher *m_fileWatcher; + QServiceInterfaceDescriptor latestDescriptor(const QList<QServiceInterfaceDescriptor> &descriptors); + + bool m_hasAccessedUserDb; + bool m_alreadyWarnedOpenError; +}; + + +class Q_AUTOTEST_EXPORT DatabaseFileWatcher : public QObject +{ + Q_OBJECT +public: + DatabaseFileWatcher(DatabaseManager *parent = 0); + + void setEnabled(ServiceDatabase *database, bool enabled); + +private slots: + void databaseChanged(const QString &path); + void databaseDirectoryChanged(const QString &path); + +private: + void notifyChanges(ServiceDatabase *database, DatabaseManager::DbScope scope); + QString closestExistingParent(const QString &path); + void restartDirMonitoring(const QString &dbPath, const QString &previousDirPath); + + DatabaseManager *m_manager; + QFileSystemWatcher *m_watcher; + QHash<QString, QStringList> m_knownServices; + QStringList m_monitoredDbPaths; +}; + +QTM_END_NAMESPACE +QT_END_HEADER + +#endif diff --git a/src/serviceframework/databasemanager_symbian.cpp b/src/serviceframework/databasemanager_symbian.cpp new file mode 100644 index 00000000..575f4f88 --- /dev/null +++ b/src/serviceframework/databasemanager_symbian.cpp @@ -0,0 +1,541 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined(__WINS__) && !defined(SYMBIAN_EMULATOR_SUPPORTS_PERPROCESS_WSD) +//Use DatabaseManager "directly" in emulators where per process WSD is not +//supported. +#include "databasemanager.cpp" +#include "servicedatabase.cpp" +#else //defined(__WINS__) && !defined(SYMBIAN_EMULATOR_SUPPORTS_PERPROCESS_WSD) + +#ifdef QTM_BUILD_UNITTESTS +#include "servicedatabase.cpp" +#endif + +#include "databasemanager_symbian_p.h" +#include "clientservercommon.h" +#include <qserviceinterfacedescriptor_p.h> +#include <qserviceinterfacedescriptor.h> +#include <qservicefilter.h> +#include <QProcess> +#include <QDebug> +#include <s32mem.h> + +QTM_BEGIN_NAMESPACE + +/* + \class DatabaseManager + \ingroup servicesfw + \brief The database manager is responsible for receiving queries + about services and managing user and system scope databases in order to + respond to those queries. + + The DatabaseManager provides operations for + - registering and unregistering services + - querying for services and interfaces + - setting and getting default interface implementations + + and provides notifications by emitting signals for added + or removed services. + + Implementation note: + When one of the above operations is first invoked a connection with the + appropriate database(s) is opened. This connection remains + open until the DatabaseManager is destroyed. + + If the system scope database cannot be opened when performing + user scope operations. The operations are carried out as per normal + but only acting on the user scope database. Each operation invokation + will try to open a connection with the system scope database. + + Terminology note: + When referring to user scope regarding operations, it generally + means access to both the user and system databases with the + data from both combined into a single dataset. + When referring to a user scope database it means the + user database only. +*/ + +/* + \fn DatabaseManager::DatabaseManager() + + Constructor +*/ +DatabaseManager::DatabaseManager() +{ + TInt err = iSession.Connect(); + if (err != KErrNone) + qt_symbian_throwIfError(err); + + iDatabaseManagerSignalMonitor = new DatabaseManagerSignalMonitor(*this, iSession); +} + +/* + \fn DatabaseManager::~DatabaseManager() + + Destructor +*/ +DatabaseManager::~DatabaseManager() +{ + delete iDatabaseManagerSignalMonitor; + iSession.Close(); +} + +/* + \fn bool DatabaseManager::registerService(ServiceMetaDataResults &service, DbScope scope) + + Adds the details \a service into the service database corresponding to + \a scope. + + Returns true if the operation succeeded and false otherwise. + The last error is set when this function is called. +*/ +bool DatabaseManager::registerService(ServiceMetaDataResults &service, DbScope scope) +{ + Q_UNUSED(scope); + return iSession.RegisterService(service); +} + +/* + \fn bool DatabaseManager::unregisterService(const QString &serviceName, DbScope scope) + + Removes the details of \a serviceName from the database corresponding to \a + scope. + + Returns true if the operation succeeded, false otherwise. + The last error is set when this function is called. +*/ +bool DatabaseManager::unregisterService(const QString &serviceName, DbScope scope) +{ + Q_UNUSED(scope); + return iSession.UnregisterService(serviceName); +} + +/* + Removes the initialization specific information of \serviceName from the database. + + Returns true if the operation succeeded, false otherwise. + The last error is set when this function is called. + */ +bool DatabaseManager::serviceInitialized(const QString &serviceName, DbScope scope) +{ + Q_UNUSED(scope); + return iSession.ServiceInitialized(serviceName); +} + +/* + \fn QList<QServiceInterfaceDescriptor> DatabaseManager::getInterfaces(const QServiceFilter &filter, DbScope scope) + + Retrieves a list of interface descriptors that fulfill the constraints specified + by \a filter at a given \a scope. + + The last error is set when this function is called. +*/ +QList<QServiceInterfaceDescriptor> DatabaseManager::getInterfaces(const QServiceFilter &filter, DbScope scope) +{ + Q_UNUSED(scope); + return iSession.Interfaces(filter); +} + + +/* + \fn QStringList DatabaseManager::getServiceNames(const QString &interfaceName, DbScope scope) + + Retrieves a list of the names of services that provide the interface + specified by \a interfaceName. + + The last error is set when this function is called. +*/ +QStringList DatabaseManager::getServiceNames(const QString &interfaceName, DbScope scope) +{ + Q_UNUSED(scope); + return iSession.ServiceNames(interfaceName); +} + +/* + \fn QServiceInterfaceDescriptor DatabaseManager::interfaceDefault(const QString &interfaceName, DbScope scope) + + Returns the default interface implementation descriptor for a given + \a interfaceName and \a scope. + + The last error is set when this function is called. +*/ +QServiceInterfaceDescriptor DatabaseManager::interfaceDefault(const QString &interfaceName, DbScope scope) +{ + Q_UNUSED(scope); + return iSession.InterfaceDefault(interfaceName); +} + +/* + \fn bool DatabaseManager::setInterfaceDefault(const QString &serviceName, const QString &interfaceName, DbScope scope) + + Sets the default interface implemenation for \a interfaceName to the matching + interface implementation provided by \a service. + + If \a service provides more than one interface implementation, the newest + version of the interface is set as the default. + + Returns true if the operation was succeeded, false otherwise + The last error is set when this function is called. +*/ +bool DatabaseManager::setInterfaceDefault(const QString &serviceName, const + QString &interfaceName, DbScope scope) { + Q_UNUSED(scope); + return iSession.SetInterfaceDefault(serviceName, interfaceName); +} + +/* + \fn bool DatabaseManager::setInterfaceDefault(const QServiceInterfaceDescriptor &descriptor, DbScope scope) + + Sets the interface implementation specified by \a descriptor to be the default + implementation for the particular interface specified in the descriptor. + + Returns true if the operation succeeded, false otherwise. + The last error is set when this function is called. +*/ +bool DatabaseManager::setInterfaceDefault(const QServiceInterfaceDescriptor &descriptor, DbScope scope) +{ + Q_UNUSED(scope); + return iSession.SetInterfaceDefault(descriptor); +} + +/* + \fn void DatabaseManager::setChangeNotificationsEnabled(DbScope scope, bool enabled) + + Sets whether change notifications for added and removed services are + \a enabled or not at a given \a scope. +*/ +void DatabaseManager::setChangeNotificationsEnabled(DbScope scope, bool enabled) +{ + Q_UNUSED(scope); + iSession.SetChangeNotificationsEnabled(enabled); +} + +DatabaseManagerSignalMonitor::DatabaseManagerSignalMonitor( + DatabaseManager& databaseManager, RDatabaseManagerSession& databaseManagerSession) : + CActive(EPriorityNormal), + iDatabaseManager(databaseManager), iDatabaseManagerSession(databaseManagerSession) +{ + CActiveScheduler::Add(this); + issueNotifyServiceSignal(); +} + +DatabaseManagerSignalMonitor::~DatabaseManagerSignalMonitor() +{ + Cancel(); +} + +void DatabaseManagerSignalMonitor::issueNotifyServiceSignal() +{ + iDatabaseManagerSession.NotifyServiceSignal(iStatus); + SetActive(); +} + +DBError DatabaseManager::lastError() +{ + return iSession.LastError(); +} + +void DatabaseManagerSignalMonitor::DoCancel() +{ + iDatabaseManagerSession.CancelNotifyServiceSignal(); +} + +void DatabaseManagerSignalMonitor::RunL() +{ + switch (iStatus.Int()) + { + case ENotifySignalComplete: + { + QString serviceName = QString::fromUtf16(iDatabaseManagerSession.iServiceName.Ptr(), iDatabaseManagerSession.iServiceName.Length()); + + if ((DatabaseManager::State)iDatabaseManagerSession.iState() == DatabaseManager::EAdded) + { + emit iDatabaseManager.serviceAdded(serviceName, DatabaseManager::SystemScope); + } + else if ((DatabaseManager::State)iDatabaseManagerSession.iState() == DatabaseManager::ERemoved) + { + emit iDatabaseManager.serviceRemoved(serviceName, DatabaseManager::SystemScope); + } + issueNotifyServiceSignal(); + break; + } + default: + { + + } + break; + } +} + + +RDatabaseManagerSession::RDatabaseManagerSession() + : RSessionBase() + { + } + +TVersion RDatabaseManagerSession::Version() const + { + return TVersion(KServerMajorVersionNumber, KServerMinorVersionNumber, KServerBuildVersionNumber); + } + +TInt RDatabaseManagerSession::Connect() + { + TInt retryCount = 2; + for (;;) + { + TInt err = CreateSession(KDatabaseManagerServerName, TVersion(), 8, EIpcSession_Sharable); + if (err != KErrNotFound && err != KErrServerTerminated) + return err; + if (--retryCount == 0) + return err; + err = StartServer(); + if (err != KErrNone && err != KErrAlreadyExists) + return err; + } + } + +TInt RDatabaseManagerSession::StartServer() + { + TInt ret = KErrNone; + TFindServer findServer(KDatabaseManagerServerName); + TFullName name; + + if (findServer.Next(name) != KErrNone) + { + TRequestStatus status; + RProcess dbServer; + ret = dbServer.Create(KDatabaseManagerServerProcess, KNullDesC); + if(ret != KErrNone) + { + return ret; + } + dbServer.Rendezvous(status); + if(status != KRequestPending) + { + dbServer.Kill(KErrNone); + dbServer.Close(); + return KErrGeneral; + } + else + { + dbServer.Resume(); + } + + User::WaitForRequest(status); + if(status != KErrNone) + { + dbServer.Close(); + return status.Int(); + } + dbServer.Close(); + } + + return ret; + } + +void RDatabaseManagerSession::Close() + { + RSessionBase::Close(); + } + +bool RDatabaseManagerSession::RegisterService(ServiceMetaDataResults& aService) + { + QByteArray serviceByteArray; + QDataStream in(&serviceByteArray, QIODevice::WriteOnly); + in.setVersion(QDataStream::Qt_4_6); + in << aService; + TPtrC8 ptr8((TUint8*)(serviceByteArray.constData()), serviceByteArray.size()); + TIpcArgs args(&ptr8, &iError); + SendReceive(ERegisterServiceRequest, args); + + return (iError() == DBError::NoError); + } + +bool RDatabaseManagerSession::UnregisterService(const QString& aServiceName) + { + TPtrC serviceNamePtr(reinterpret_cast<const TUint16*>(aServiceName.utf16())); + TIpcArgs args(&serviceNamePtr, &iError); + SendReceive(EUnregisterServiceRequest, args); + + return (iError() == DBError::NoError); + } + +bool RDatabaseManagerSession::ServiceInitialized(const QString& aServiceName) + { + TPtrC serviceNamePtr(reinterpret_cast<const TUint16*>(aServiceName.utf16())); + TIpcArgs args(&serviceNamePtr, &iError); + SendReceive(EServiceInitializedRequest, args); + + return (iError() == DBError::NoError); + } + + +QList<QServiceInterfaceDescriptor> RDatabaseManagerSession::Interfaces(const QServiceFilter& aFilter) + { + QByteArray filterByteArray; + QDataStream in(&filterByteArray, QIODevice::WriteOnly); + in.setVersion(QDataStream::Qt_4_6); + in << aFilter; + TPtrC8 ptr8((TUint8*)(filterByteArray.constData()), filterByteArray.size()); + TPckgBuf<TInt> lengthPckg(0); + TIpcArgs args(&ptr8, &lengthPckg, &iError); + SendReceive(EGetInterfacesSizeRequest, args); + + HBufC8* descriptorListBuf = HBufC8::New(lengthPckg()); + TPtr8 ptrToBuf(descriptorListBuf->Des()); + TIpcArgs args2(&ptrToBuf); + SendReceive(EGetInterfacesRequest, args2); + + QByteArray descriptorListByteArray((const char*)ptrToBuf.Ptr(), ptrToBuf.Length()); + QDataStream out(descriptorListByteArray); + QList<QServiceInterfaceDescriptor> descriptorList; + out >> descriptorList; + + delete descriptorListBuf; + + return descriptorList; + } + +QStringList RDatabaseManagerSession::ServiceNames(const QString& aInterfaceName) + { + TPtrC interfaceNamePtr(reinterpret_cast<const TUint16*>(aInterfaceName.utf16())); + HBufC* interfaceNamebuf = HBufC::New(interfaceNamePtr.Length()); + interfaceNamebuf->Des().Copy(interfaceNamePtr); + TPckgBuf<TInt> lengthPckg(0); + TIpcArgs args(interfaceNamebuf, &lengthPckg, &iError); + SendReceive(EGetServiceNamesSizeRequest, args); + + HBufC8* serviceNamesBuf = HBufC8::New(lengthPckg()); + TPtr8 ptrToBuf(serviceNamesBuf->Des()); + TIpcArgs args2(&ptrToBuf); + SendReceive(EGetServiceNamesRequest, args2); + + QByteArray nameListByteArray((const char*)ptrToBuf.Ptr(), ptrToBuf.Length()); + QDataStream out(nameListByteArray); + QStringList nameList; + out >> nameList; + + delete interfaceNamebuf; + delete serviceNamesBuf; + + return nameList; + } + +QServiceInterfaceDescriptor RDatabaseManagerSession::InterfaceDefault(const QString& aInterfaceName) + { + TPtrC interfaceNamePtr(reinterpret_cast<const TUint16*>(aInterfaceName.utf16())); + HBufC* interfaceNameBuf = HBufC::New(interfaceNamePtr.Length()); + interfaceNameBuf->Des().Copy(interfaceNamePtr); + TPckgBuf<TInt> lengthPckg(0); + TIpcArgs args(interfaceNameBuf, &lengthPckg, &iError); + SendReceive(EInterfaceDefaultSizeRequest, args); + + HBufC8* interfaceDescriptorBuf = HBufC8::New(lengthPckg()); + TPtr8 ptrToBuf(interfaceDescriptorBuf->Des()); + TIpcArgs args2(&ptrToBuf); + SendReceive(EInterfaceDefaultRequest, args2); + + QByteArray interfaceDescriptorByteArray((const char*)interfaceDescriptorBuf->Ptr(), interfaceDescriptorBuf->Length()); + QDataStream out(interfaceDescriptorByteArray); + QServiceInterfaceDescriptor interfaceDescriptor; + out >> interfaceDescriptor; + + delete interfaceNameBuf; + delete interfaceDescriptorBuf; + + return interfaceDescriptor; + } + +bool RDatabaseManagerSession::SetInterfaceDefault(const QString &aServiceName, const QString &aInterfaceName) + { + TPtrC serviceNamePtr(reinterpret_cast<const TUint16*>(aServiceName.utf16())); + TPtrC interfaceNamePtr(reinterpret_cast<const TUint16*>(aInterfaceName.utf16())); + TIpcArgs args(&serviceNamePtr, &interfaceNamePtr, &iError); + SendReceive(ESetInterfaceDefault, args); + + return (iError() == DBError::NoError); + } + +bool RDatabaseManagerSession::SetInterfaceDefault(const QServiceInterfaceDescriptor &aInterface) + { + QByteArray interfaceByteArray; + QDataStream in(&interfaceByteArray, QIODevice::WriteOnly); + in.setVersion(QDataStream::Qt_4_6); + in << aInterface; + TPtrC8 ptr8((TUint8 *)(interfaceByteArray.constData()), interfaceByteArray.size()); + TIpcArgs args(&ptr8, &iError); + SendReceive(ESetInterfaceDefault2, args); + + return (iError() == DBError::NoError); + } + +DBError RDatabaseManagerSession::LastError() + { + DBError error; + error.setError((DBError::ErrorCode)iError()); + return error; + } + +void RDatabaseManagerSession::SetChangeNotificationsEnabled(bool aEnabled) + { + TIpcArgs args((aEnabled ? 1 : 0), &iError); + SendReceive(ESetChangeNotificationsEnabledRequest, args); + } + +void RDatabaseManagerSession::NotifyServiceSignal(TRequestStatus& aStatus) + { + iArgs.Set(0, &iServiceName); + iArgs.Set(1, &iState); + iArgs.Set(2, &iError); + SendReceive(ENotifyServiceSignalRequest, iArgs, aStatus); + } + +void RDatabaseManagerSession::CancelNotifyServiceSignal() const + { + SendReceive(ECancelNotifyServiceSignalRequest, TIpcArgs(NULL)); + } + +#include "moc_databasemanager_symbian_p.cpp" + +QTM_END_NAMESPACE + +#endif //defined(__WINS__) && !defined(SYMBIAN_EMULATOR_SUPPORTS_PERPROCESS_WSD) diff --git a/src/serviceframework/databasemanager_symbian_p.h b/src/serviceframework/databasemanager_symbian_p.h new file mode 100644 index 00000000..dc4a9f91 --- /dev/null +++ b/src/serviceframework/databasemanager_symbian_p.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DATABASEMANAGER_SYMBIAN_P_H +#define DATABASEMANAGER_SYMBIAN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#if defined(__WINS__) && !defined(SYMBIAN_EMULATOR_SUPPORTS_PERPROCESS_WSD) +//Use DatabaseManager "directly" in emulators where per process WSD is not +//supported. +#include "databasemanager_p.h" +#else //defined(__WINS__) && !defined(SYMBIAN_EMULATOR_SUPPORTS_PERPROCESS_WSD) + +#include "qserviceframeworkglobal.h" +#include <QObject> +#include <QList> + +#include <servicemetadata_p.h> +#include "dberror_p.h" +#include <e32base.h> + + +QT_BEGIN_HEADER +QTM_BEGIN_NAMESPACE + +class CDatabaseManagerServerThread; +class QServiceFilter; +typedef TPckgBuf<TInt> TError; + +class QServiceInterfaceDescriptor; + +class RDatabaseManagerSession : public RSessionBase + { + public: + enum DbScope{UserScope, SystemScope, UserOnlyScope}; + RDatabaseManagerSession(); + + public: + TInt Connect(); + void Close(); + TVersion Version() const; + + bool RegisterService(ServiceMetaDataResults& aService); + bool UnregisterService(const QString& aServiceName); + bool ServiceInitialized(const QString& aServiceName); + + QList<QServiceInterfaceDescriptor> Interfaces(const QServiceFilter& aFilter); + QStringList ServiceNames(const QString& aInterfaceName); + + QServiceInterfaceDescriptor InterfaceDefault(const QString& aInterfaceName); + bool SetInterfaceDefault(const QString& aServiceName, const QString& aInterfaceName); + bool SetInterfaceDefault(const QServiceInterfaceDescriptor& aInterface); + + DBError LastError(); + + void SetChangeNotificationsEnabled(bool aEnabled); + + void NotifyServiceSignal(TRequestStatus& aStatus); + void CancelNotifyServiceSignal() const; + + public: + TBuf<255> iServiceName; + TPckgBuf<TInt> iState; + + private: + TInt StartServer(); + + private: + TIpcArgs iArgs; + TError iError; + }; + +class DatabaseManagerSignalMonitor; + +class Q_AUTOTEST_EXPORT DatabaseManager : public QObject +{ + Q_OBJECT + + public: + enum DbScope{UserScope, SystemScope, UserOnlyScope}; + DatabaseManager(void); + virtual ~DatabaseManager(); + + bool registerService(ServiceMetaDataResults &service, DbScope scope); + bool unregisterService(const QString &serviceName, DbScope scope); + bool serviceInitialized(const QString &serviceName, DbScope scope); + + QList<QServiceInterfaceDescriptor> getInterfaces(const QServiceFilter &filter, DbScope scope); + QStringList getServiceNames(const QString &interfaceName, DbScope scope); + + QServiceInterfaceDescriptor interfaceDefault(const QString &interfaceName, DbScope scope); + bool setInterfaceDefault(const QString &serviceName, const QString &interfaceName, DbScope scope); + bool setInterfaceDefault(const QServiceInterfaceDescriptor &interface, DbScope scope); + + DBError lastError(); + + void setChangeNotificationsEnabled(DbScope scope, bool enabled); + + signals: + void serviceAdded(const QString &serviceName, DatabaseManager::DbScope scope); + void serviceRemoved(const QString &serviceName, DatabaseManager::DbScope scope); + + private: + enum State{EAdded, ERemoved}; + void notifyServiceSignal(); + + private: + friend class DatabaseManagerSignalMonitor; + DatabaseManagerSignalMonitor* iDatabaseManagerSignalMonitor; + RDatabaseManagerSession iSession; + QServiceInterfaceDescriptor latestDescriptor(const QList<QServiceInterfaceDescriptor> &descriptors); +}; + +class DatabaseManagerSignalMonitor : public CActive +{ + public: + DatabaseManagerSignalMonitor(DatabaseManager& databaseManager, RDatabaseManagerSession& databaseManagerSession); + ~DatabaseManagerSignalMonitor(); + + protected: + void issueNotifyServiceSignal(); + + protected: // From CActive + void DoCancel(); + void RunL(); + private: + DatabaseManager& iDatabaseManager; + RDatabaseManagerSession& iDatabaseManagerSession; +}; + +QTM_END_NAMESPACE +QT_END_HEADER + +#endif //defined(__WINS__) && !defined(SYMBIAN_EMULATOR_SUPPORTS_PERPROCESS_WSD) +#endif //DATABASEMANAGER_SYMBIAN_P_H diff --git a/src/serviceframework/databasemanagerserver_symbian/clientservercommon.h b/src/serviceframework/databasemanagerserver_symbian/clientservercommon.h new file mode 100644 index 00000000..0e88f444 --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/clientservercommon.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CLIENTSERVERCOMMON_H_ +#define CLIENTSERVERCOMMON_H_ + +#include <e32base.h> + +_LIT(KDatabaseManagerServerName, "!qsfwdatabasemanagerserver"); +_LIT(KDatabaseManagerServerProcess, "qsfwdatabasemanagerserver"); + +const TUint KServerMajorVersionNumber = 0; +const TUint KServerMinorVersionNumber = 1; +const TUint KServerBuildVersionNumber = 1; + +IMPORT_C TInt StartThread(RThread& aServerThread); + +enum TDBServerRqst +{ + ERegisterServiceRequest, + EUnregisterServiceRequest, + EServiceInitializedRequest, + EGetInterfacesRequest, + EGetServiceNamesRequest, + EGetServiceNamesSizeRequest, + EGetInterfacesSizeRequest, + EInterfaceDefaultRequest, + EInterfaceDefaultSizeRequest, + ESetInterfaceDefault, + ESetInterfaceDefault2, + ESetChangeNotificationsEnabledRequest, + ENotifyServiceSignalRequest, + ECancelNotifyServiceSignalRequest +}; + +enum TDBServerRqstComplete +{ + ENotifySignalComplete = 1 +}; + +#endif // CLIENTSERVERCOMMON_H_ + +// End of file diff --git a/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver.cpp b/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver.cpp new file mode 100644 index 00000000..bc7107f1 --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "databasemanagerserver_p.h" +#include "clientservercommon.h" +#include "databasemanagersession_p.h" +#include "servicedatabase_p.h" + +#include <QFileSystemWatcher> + +#include <QCoreApplication> +//#include <QThread> + +QTM_BEGIN_NAMESPACE + +#define SEC_TOKEN 0x00000000 + +static TInt Timeout(TAny *aObject); + +const TInt CDatabaseManagerServer::timeoutInterval = 30000000; // 30 seconds + +TInt Timeout(TAny *aObject) + { + ((CDatabaseManagerServer *)aObject)->Shutdown(); + return 1; + } + +CDatabaseManagerServer::CDatabaseManagerServer() + : CServer2(EPriorityNormal, ESharableSessions) + , iSessionCount(0) + { + iPeriodic = CPeriodic::NewL(0); + iPeriodic->Start(timeoutInterval, timeoutInterval, TCallBack(Timeout, this)); + iDb = new ServiceDatabase(); + initDbPath(); + + iDatabaseManagerServerSignalHandler = new DatabaseManagerServerSignalHandler(this); + iWatcher = new QFileSystemWatcher(); + QObject::connect(iWatcher, SIGNAL(directoryChanged(QString)), + iDatabaseManagerServerSignalHandler, SLOT(importChanged(QString))); + + QString path = QDir::toNativeSeparators(QCoreApplication::applicationDirPath()); + path += QDir::separator() + QString("import"); + + // Make the directory incase no xml services are installed + QDir dir; + dir.mkdir(path); + iWatcher->addPath(path); + + DiscoverServices(); + + } + +CSession2* CDatabaseManagerServer::NewSessionL(const TVersion& aVersion, const RMessage2& /*aMessage*/) const + { + if (!User::QueryVersionSupported(TVersion(KServerMajorVersionNumber, + KServerMinorVersionNumber, KServerBuildVersionNumber), aVersion)) + { + User::Leave(KErrNotSupported); + } + + return CDatabaseManagerServerSession::NewL(*const_cast<CDatabaseManagerServer*>(this), iDb->databasePath()); + } + +void CDatabaseManagerServer::PanicServer(TDatabaseManagerSerververPanic aPanic) + { + _LIT(KTxtServerPanic,"Database manager server panic"); + User::Panic(KTxtServerPanic, aPanic); + } + +void CDatabaseManagerServer::IncreaseSessions() + { + iSessionCount++; + iPeriodic->Cancel(); + } + +void CDatabaseManagerServer::DecreaseSessions() + { + iSessionCount--; + if (iSessionCount <= 0) + { + iPeriodic->Start(timeoutInterval, timeoutInterval, TCallBack(Timeout, this)); + } + } + +void CDatabaseManagerServer::Shutdown() + { + QCoreApplication::exit(0); + } + +void CDatabaseManagerServer::initDbPath() + { + QString dbIdentifier = "_system"; + + QDir dir(QDir::toNativeSeparators(QCoreApplication::applicationDirPath())); + QString qtVersion(qVersion()); + qtVersion = qtVersion.left(qtVersion.size() -2); //strip off patch version + QString dbName = QString("QtServiceFramework_") + qtVersion + dbIdentifier + QLatin1String(".db"); + iDb->setDatabasePath(dir.path() + QDir::separator() + dbName); + + // check if database is copied from Z drive; also valid for emulator + QFile dbFile(iDb->databasePath()); + QFileInfo dbFileInfo(dbFile); + if (!dbFileInfo.exists()) { + // create folder first + if (!dbFileInfo.dir().exists()) + QDir::root().mkpath(dbFileInfo.path()); + // copy file from ROM + QFile romDb(QLatin1String("z:\\private\\2002ac7f\\") + dbFileInfo.fileName()); + // why not use QFile::copy? + if (romDb.open(QIODevice::ReadOnly) && dbFile.open(QFile::WriteOnly)) { + QByteArray data = romDb.readAll(); + dbFile.write(data); + dbFile.close(); + romDb.close(); + } + } + + iDb->open(); + } + +void CDatabaseManagerServer::DiscoverServices() +{ + QString path = QDir::toNativeSeparators(QCoreApplication::applicationDirPath()); + QSettings settings(path + QDir::separator() + "autoimport.ini", + QSettings::NativeFormat); + + QString imports = path + QDir::separator() + "import"; + + QDir dir(imports); + dir.setFilter(QDir::Files); + QStringList filters; + filters << "*.xml"; + dir.setNameFilters(filters); + QSet<QString> seen; + + QString tok = QString::number(SEC_TOKEN); + + QStringList files = dir.entryList(); + while(!files.isEmpty()){ + QString file = files.takeFirst(); + seen << file; + + QFileInfo fileinfo(imports + QDir::separator() + file); + + if(settings.contains(file)){ + if(fileinfo.lastModified() == settings.value(file).toDateTime()) { + continue; + } + } + QFile *f = new QFile(imports + QDir::separator() + file); + // read contents, register + ServiceMetaData parser(f); + if (!parser.extractMetadata()) { + f->remove(); + f->close(); + continue; + } + const ServiceMetaDataResults data = parser.parseResults(); + ServiceMetaDataResults results = parser.parseResults(); + QString servicename = results.name; + + if(iDb->registerService(results, tok)){ + iDb->serviceInitialized(results.name, tok); + } + f->close(); + settings.setValue(file, fileinfo.lastModified()); + settings.setValue(file + "/service_name", servicename); + } + + QSet<QString> oldfiles = settings.allKeys().toSet(); + oldfiles -= seen; + foreach(QString old, oldfiles){ + if(old.contains('/')) + continue; + QString servicename = settings.value(old + "/service_name").toString(); + iDb->unregisterService(servicename, QString("Auto Registration")); + settings.remove(old); + } +} + +void DatabaseManagerServerSignalHandler::importChanged(const QString& path) +{ + iDatabaseManagerServerSession->DiscoverServices(); +} + + +QTM_END_NAMESPACE + + +#include "moc_databasemanagerserver_p.cpp" +// End of File diff --git a/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver.pan b/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver.pan new file mode 100644 index 00000000..9d9a93a1 --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver.pan @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef DATABASEMANAGERSERVER_PAN_ +#define DATABASEMANAGERSERVER_PAN_ + +// Panic category +_LIT(KDatabaseManagerServer, "DatabaseManagerServer"); +_LIT(KUnknownOpCode, "Unknown op code"); +_LIT(KBadDescriptor, "Bad descriptor"); + +enum TDatabaseManagerSerververPanic +{ + EBadRequest = 1, + ESrvCreateServer = 2, + ESvrStartServer = 3 +}; + +#endif + +// End of File diff --git a/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver.pro b/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver.pro new file mode 100644 index 00000000..d52d60ac --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver.pro @@ -0,0 +1,43 @@ +TEMPLATE = app +TARGET = qsfwdatabasemanagerserver +QT = core sql +TARGET.UID3 = 0x2002AC7F + +CONFIG += no_icon + +DEFINES += QTM_SERVICEFW_SYMBIAN_DATABASEMANAGER_SERVER +DEFINES += QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + +SOURCES += databasemanagerservermain.cpp + +include(../../../common.pri) + + +DEPENDPATH += ../ +INCLUDEPATH += ../ + +HEADERS += servicemetadata_p.h \ + servicedatabase_p.h \ + qserviceplugininterface.h \ + qabstractsecuritysession.h \ + qserviceinterfacedescriptor.h \ + qserviceinterfacedescriptor_p.h \ + qservicefilter.h \ + dberror_p.h \ + databasemanagerserver_p.h \ + databasemanagersession_p.h \ + databasemanagersignalhandler_p.h + +SOURCES += servicemetadata.cpp \ + servicedatabase.cpp \ + qserviceplugininterface.cpp \ + qabstractsecuritysession.cpp \ + qserviceinterfacedescriptor.cpp \ + qservicefilter.cpp \ + dberror.cpp \ + databasemanagerserver.cpp \ + databasemanagersession.cpp \ + databasemanagersignalhandler.cpp + +#ProtServ is needed so that the server can be in protected namespace (start with '!' -mark). +TARGET.CAPABILITY = ProtServ diff --git a/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver_p.h b/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver_p.h new file mode 100644 index 00000000..5f57e9af --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/databasemanagerserver_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CDATABASEMANAGERSERVER_H_ +#define CDATABASEMANAGERSERVER_H_ + +#include <qserviceframeworkglobal.h> +#include <e32base.h> +#include <QObject> +#include "databasemanagerserver.pan" + +class QFileSystemWatcher; + +//QTM_BEGIN_NAMESPACE +namespace QtMobility { + +class DatabaseManagerServerSignalHandler; +class ServiceDatabase; + + +// needed for creating server thread. +const TUint KDefaultHeapSize = 0x10000; + +class CDatabaseManagerServer : public CServer2 + { + public: + CDatabaseManagerServer(); + CSession2* NewSessionL(const TVersion& aVersion, const RMessage2& aMessage) const; + + public: + static void PanicServer(TDatabaseManagerSerververPanic aPanic); + + void IncreaseSessions(); + void DecreaseSessions(); + + void Shutdown(); + + void DiscoverServices(); + + private: + void initDbPath(); + + static const TInt timeoutInterval; + int iSessionCount; + CPeriodic *iPeriodic; + ServiceDatabase *iDb; + QFileSystemWatcher *iWatcher; + DatabaseManagerServerSignalHandler *iDatabaseManagerServerSignalHandler; + }; + + +class DatabaseManagerServerSignalHandler : public QObject + { + Q_OBJECT + + public: + DatabaseManagerServerSignalHandler(CDatabaseManagerServer *databaseManagerServerSession) + { + iDatabaseManagerServerSession = databaseManagerServerSession; + } + + public Q_SLOTS: + void importChanged(const QString &path); + + public: + CDatabaseManagerServer *iDatabaseManagerServerSession; + }; + +//QTM_END_NAMESPACE +} + +#endif + +// End of File diff --git a/src/serviceframework/databasemanagerserver_symbian/databasemanagerservermain.cpp b/src/serviceframework/databasemanagerserver_symbian/databasemanagerservermain.cpp new file mode 100644 index 00000000..86312746 --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/databasemanagerservermain.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qserviceframeworkglobal.h> +#include <QCoreApplication> +#include <QFile> +#include <QTextStream> +#include "databasemanagerserver_p.h" +#include "clientservercommon.h" + +QTM_USE_NAMESPACE + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + CDatabaseManagerServer* server = new CDatabaseManagerServer; + TInt err = server->Start(KDatabaseManagerServerName); + if (err != KErrAlreadyExists) + { + if (err != KErrNone) + { + CDatabaseManagerServer::PanicServer(ESvrStartServer); + } + RProcess::Rendezvous(err); + + return app.exec(); + } + return 0; +} + diff --git a/src/serviceframework/databasemanagerserver_symbian/databasemanagersession.cpp b/src/serviceframework/databasemanagerserver_symbian/databasemanagersession.cpp new file mode 100644 index 00000000..b5944f4d --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/databasemanagersession.cpp @@ -0,0 +1,614 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "clientservercommon.h" +#include "databasemanagersession_p.h" +#include "databasemanagerserver.pan" +#include "databasemanagersignalhandler_p.h" +#include "servicedatabase_p.h" +#include "databasemanagerserver_p.h" + +#include <QFileSystemWatcher> + +QTM_BEGIN_NAMESPACE + +bool lessThan(const QServiceInterfaceDescriptor &d1, + const QServiceInterfaceDescriptor &d2) + { + return (d1.majorVersion() < d2.majorVersion()) + || ( d1.majorVersion() == d2.majorVersion() + && d1.minorVersion() < d2.minorVersion()); + } + +CDatabaseManagerServerSession* CDatabaseManagerServerSession::NewL(CDatabaseManagerServer& aServer, QString dbPath) + { + CDatabaseManagerServerSession* self = CDatabaseManagerServerSession::NewLC(aServer, dbPath); + CleanupStack::Pop(self); + return self; + } + +CDatabaseManagerServerSession* CDatabaseManagerServerSession::NewLC(CDatabaseManagerServer& aServer, QString dbPath) + { + CDatabaseManagerServerSession* self = new (ELeave) CDatabaseManagerServerSession(aServer); + CleanupStack::PushL(self); + self->ConstructL(dbPath); + return self; + } + +void CDatabaseManagerServerSession::ConstructL(QString dbPath) + { + iDb = new ServiceDatabase(); + iDb->setDatabasePath(dbPath); + openDb(); + + //initDbPath(); + iDatabaseManagerSignalHandler = new DatabaseManagerSignalHandler(*this); + + m_watcher = new QFileSystemWatcher(); + QObject::connect(m_watcher, SIGNAL(fileChanged(QString)), + iDatabaseManagerSignalHandler, SLOT(databaseChanged(QString))); + } + +CDatabaseManagerServerSession::CDatabaseManagerServerSession(CDatabaseManagerServer& aServer) + : iServer(aServer), + iDatabaseManagerSignalHandler(NULL), + iDb(NULL), + m_watcher(NULL) + { + iServer.IncreaseSessions(); + } + +CDatabaseManagerServerSession::~CDatabaseManagerServerSession() + { + delete iDatabaseManagerSignalHandler; + delete iDb; + delete iByteArray; + delete m_watcher; + iServer.DecreaseSessions(); + } + +void CDatabaseManagerServerSession::ServiceL(const RMessage2& aMessage) + { + if (aMessage.Function() == ENotifyServiceSignalRequest) + { + NotifyServiceSignal(aMessage); + } + else + { + TRAPD(err, DispatchMessageL(aMessage)); + aMessage.Complete(err); + } + } + +void CDatabaseManagerServerSession::DispatchMessageL(const RMessage2& aMessage) + { + switch (aMessage.Function()) + { + case ERegisterServiceRequest: + User::LeaveIfError(RegisterServiceL(aMessage)); + break; + case EUnregisterServiceRequest: + User::LeaveIfError(UnregisterServiceL(aMessage)); + break; + case EServiceInitializedRequest: + User::LeaveIfError(ServiceInitializedL(aMessage)); + break; + case EGetInterfacesRequest: + User::LeaveIfError(InterfacesL(aMessage)); + break; + case EGetInterfacesSizeRequest: + User::LeaveIfError(InterfacesSizeL(aMessage)); + break; + case EGetServiceNamesRequest: + User::LeaveIfError(ServiceNamesL(aMessage)); + break; + case EGetServiceNamesSizeRequest: + User::LeaveIfError(ServiceNamesSizeL(aMessage)); + break; + case EInterfaceDefaultRequest: + User::LeaveIfError(InterfaceDefaultL(aMessage)); + break; + case ESetInterfaceDefault: + User::LeaveIfError(SetInterfaceDefaultL(aMessage)); + break; + case ESetInterfaceDefault2: + User::LeaveIfError(SetInterfaceDefault2L(aMessage)); + break; + case ESetChangeNotificationsEnabledRequest: + SetChangeNotificationsEnabled(aMessage); + break; + case EInterfaceDefaultSizeRequest: + User::LeaveIfError(InterfaceDefaultSize(aMessage)); + break; + case ECancelNotifyServiceSignalRequest: + User::LeaveIfError(CancelNotifyServiceSignal(aMessage)); + break; + default: + aMessage.Panic(KUnknownOpCode, KErrNotSupported); + break; + } + } + +TInt CDatabaseManagerServerSession::InterfaceDefaultSize(const RMessage2& aMessage) + { + TInt ret; + HBufC* defaultInterfaceBuf = HBufC::New(aMessage.GetDesLength(0)); + if (!defaultInterfaceBuf) + return KErrNoMemory; + + TPtr ptrToBuf(defaultInterfaceBuf->Des()); + TRAP(ret, aMessage.ReadL(0, ptrToBuf)); + + + QString interfaceName = QString::fromUtf16(ptrToBuf.Ptr(), ptrToBuf.Length()); + QServiceInterfaceDescriptor descriptor; + descriptor = iDb->interfaceDefault(interfaceName); + + iByteArray = new QByteArray(); + + QDataStream in(iByteArray, QIODevice::WriteOnly); + in.setVersion(QDataStream::Qt_4_6); + in << descriptor; + + TPckgBuf<TInt> size(iByteArray->size()); + + aMessage.Write(1, size); + aMessage.Write(2, LastErrorCode()); + delete defaultInterfaceBuf; + + return ret; + } + +TInt CDatabaseManagerServerSession::InterfaceDefaultL(const RMessage2& aMessage) + { + TPtrC8 defaultInterfacePtr8((TUint8*)(iByteArray->constData()), iByteArray->size()); + aMessage.Write(0, defaultInterfacePtr8); + delete iByteArray; + iByteArray = NULL; + + return 0; + } + +void CDatabaseManagerServerSession::NotifyServiceSignal(const RMessage2& aMessage) + { + iMsg = aMessage; + iWaitingAsyncRequest = ETrue; + } + +TInt CDatabaseManagerServerSession::CancelNotifyServiceSignal(const RMessage2& /*aMessage*/) + { + if (iWaitingAsyncRequest) + { + iMsg.Complete(KErrCancel); + iWaitingAsyncRequest = EFalse; + } + + return KErrNone; + } + +TInt CDatabaseManagerServerSession::RegisterServiceL(const RMessage2& aMessage) + { + QString securityToken; + + TVendorId vendorId = aMessage.VendorId(); + if (vendorId != 0) + { + securityToken = QString::number(vendorId); + } + else + { + securityToken = QString::number(aMessage.SecureId().iId); + } + + TInt ret; + HBufC8* serviceMetaDataBuf8 = HBufC8::New(aMessage.GetDesLength(0)); + if (!serviceMetaDataBuf8) + return KErrNoMemory; + + TPtr8 ptrToBuf(serviceMetaDataBuf8->Des()); + TRAP(ret, aMessage.ReadL(0, ptrToBuf)); + if (ret != KErrNone) + { + iDb->lastError().setError(DBError::UnknownError); + aMessage.Write(1, LastErrorCode()); + delete serviceMetaDataBuf8; + return ret; + } + + QByteArray byteArray((const char*)ptrToBuf.Ptr(), ptrToBuf.Length()); + QDataStream out(byteArray); + ServiceMetaDataResults results; + out >> results; + + iDb->registerService(results, securityToken); + + aMessage.Write(1, LastErrorCode()); + + delete serviceMetaDataBuf8; + + return ret; + } + +TInt CDatabaseManagerServerSession::UnregisterServiceL(const RMessage2& aMessage) + { + QString securityToken; + + TVendorId vendorId = aMessage.VendorId(); + if (vendorId != 0) + { + securityToken = QString::number(vendorId); + } + else + { + securityToken = QString::number(aMessage.SecureId().iId); + } + + TInt ret; + HBufC* serviceNameBuf = HBufC::New(aMessage.GetDesLength(0)); + if (!serviceNameBuf) + return KErrNoMemory; + + TPtr ptrToBuf(serviceNameBuf->Des()); + TRAP(ret, aMessage.ReadL(0, ptrToBuf)); + if (ret != KErrNone) + { + iDb->lastError().setError(DBError::UnknownError); + aMessage.Write(1, LastErrorCode()); + delete serviceNameBuf; + return ret; + } + + QString serviceName = QString::fromUtf16(ptrToBuf.Ptr(), ptrToBuf.Length()); + iDb->unregisterService(serviceName, securityToken); + + aMessage.Write(1, LastErrorCode()); + delete serviceNameBuf; + + return ret; + } + +TInt CDatabaseManagerServerSession::ServiceInitializedL(const RMessage2& aMessage) + { + QString securityToken; + + TVendorId vendorId = aMessage.VendorId(); + if (vendorId != 0) + { + securityToken = QString::number(vendorId); + } + else + { + securityToken = QString::number(aMessage.SecureId().iId); + } + + TInt ret; + HBufC* serviceNameBuf = HBufC::New(aMessage.GetDesLength(0)); + if (!serviceNameBuf) + return KErrNoMemory; + + TPtr ptrToBuf(serviceNameBuf->Des()); + TRAP(ret, aMessage.ReadL(0, ptrToBuf)); + if (ret != KErrNone) + { + iDb->lastError().setError(DBError::UnknownError); + aMessage.Write(1, LastErrorCode()); + delete serviceNameBuf; + return ret; + } + + QString serviceName = QString::fromUtf16(ptrToBuf.Ptr(), ptrToBuf.Length()); + iDb->serviceInitialized(serviceName, securityToken); + + aMessage.Write(1, LastErrorCode()); + delete serviceNameBuf; + + return ret; + } + +TInt CDatabaseManagerServerSession::InterfacesSizeL(const RMessage2& aMessage) + { + TInt ret; + HBufC8* buf = HBufC8::New(aMessage.GetDesLength(0)); + if (!buf) + return KErrNoMemory; + + TPtr8 ptrToBuf(buf->Des()); + TRAP(ret, aMessage.ReadL(0, ptrToBuf)); + if (ret != KErrNone) + { + iDb->lastError().setError(DBError::UnknownError); + aMessage.Write(2, LastErrorCode()); + delete buf; + return ret; + } + + QByteArray byteArray((const char*)ptrToBuf.Ptr(), ptrToBuf.Length()); + QDataStream out(byteArray); + QServiceFilter filter; + out >> filter; + + QList<QServiceInterfaceDescriptor> interfaces = iDb->getInterfaces(filter); + iByteArray = new QByteArray(); + + QDataStream in(iByteArray, QIODevice::WriteOnly); + in.setVersion(QDataStream::Qt_4_6); + in << interfaces; + + TPckgBuf<TInt> size(iByteArray->size()); + aMessage.Write(1, size); + aMessage.Write(2, LastErrorCode()); + + delete buf; + return ret; + } + +TInt CDatabaseManagerServerSession::InterfacesL(const RMessage2& aMessage) + { + TPtrC8 interfacesPtr8((TUint8*)(iByteArray->constData()), iByteArray->size()); + aMessage.Write(0, interfacesPtr8); + delete iByteArray; + iByteArray = NULL; + + return 0; + } + +TInt CDatabaseManagerServerSession::ServiceNamesSizeL(const RMessage2& aMessage) + { + TInt ret; + HBufC* serviceNamesBuf = HBufC::New(aMessage.GetDesLength(0)); + if (!serviceNamesBuf) + return KErrNoMemory; + + TPtr ptrToBuf (serviceNamesBuf->Des()); + TRAP(ret, aMessage.ReadL(0, ptrToBuf)); + if (ret != KErrNone) + { + iDb->lastError().setError(DBError::UnknownError); + aMessage.Write(2, LastErrorCode()); + delete serviceNamesBuf; + return ret; + } + + QString interfaceName = QString::fromUtf16(ptrToBuf.Ptr(), ptrToBuf.Length()); + QStringList serviceNames = iDb->getServiceNames(interfaceName); + iByteArray = new QByteArray(); + + QDataStream in(iByteArray, QIODevice::WriteOnly); + in.setVersion(QDataStream::Qt_4_6); + in << serviceNames; + + TPckgBuf<TInt> size(iByteArray->size()); + aMessage.Write(1, size); + aMessage.Write(2, LastErrorCode()); + delete serviceNamesBuf; + + return ret; + } + +TInt CDatabaseManagerServerSession::ServiceNamesL(const RMessage2& aMessage) + { + TPtrC8 ptr8((TUint8*)(iByteArray->constData()), iByteArray->size()); + aMessage.Write(0, ptr8); + delete iByteArray; + iByteArray = NULL; + + return 0; + } + +TInt CDatabaseManagerServerSession::SetInterfaceDefaultL(const RMessage2& aMessage) + { + TInt ret; + HBufC* serviceNameBuf = HBufC::New(aMessage.GetDesLength(0)); + HBufC* interfaceNameBuf = HBufC::New(aMessage.GetDesLength(1)); + if (!serviceNameBuf || !interfaceNameBuf) + return KErrNoMemory; + + TPtr ptrToBuf(serviceNameBuf->Des()); + TPtr ptrToBuf2(interfaceNameBuf->Des()); + + TRAP(ret, aMessage.ReadL(0, ptrToBuf)); + TRAPD(ret2, aMessage.ReadL(1, ptrToBuf2)); + if (ret != KErrNone || ret2 != KErrNone) + { + iDb->lastError().setError(DBError::UnknownError); + aMessage.Write(2, LastErrorCode()); + delete serviceNameBuf; + delete interfaceNameBuf; + return (ret == KErrNone) ? ret2 : ret; + } + + QString serviceName = QString::fromUtf16(ptrToBuf.Ptr(), ptrToBuf.Length()); + QString interfaceName = QString::fromUtf16(ptrToBuf2.Ptr(), ptrToBuf2.Length()); + + QList<QServiceInterfaceDescriptor> descriptors; + QServiceFilter filter; + filter.setServiceName(serviceName); + filter.setInterface(interfaceName); + // Nothing should be returned, because we are checking on nonexistent service + descriptors = iDb->getInterfaces(filter); + + //find the descriptor with the latest version + int latestIndex = 0; + for (int i = 1; i < descriptors.count(); ++i) { + if (lessThan(descriptors[latestIndex], descriptors[i])) + latestIndex = i; + } + + if (!descriptors.isEmpty()) { + iDb->setInterfaceDefault(descriptors[latestIndex]); + } + else { + aMessage.Write(2, TError(DBError::NotFound)); + delete serviceNameBuf; + delete interfaceNameBuf; + return KErrNotFound; + } + + aMessage.Write(2, LastErrorCode()); + delete serviceNameBuf; + delete interfaceNameBuf; + + return ret; + } + +TInt CDatabaseManagerServerSession::SetInterfaceDefault2L(const RMessage2& aMessage) + { + TInt ret; + HBufC8* interfaceBuf = HBufC8::New(aMessage.GetDesLength(0)); + if (!interfaceBuf) + return KErrNoMemory; + + TPtr8 ptrToBuf(interfaceBuf->Des()); + TRAP(ret, aMessage.ReadL(0, ptrToBuf)); + if (ret != KErrNone) + { + iDb->lastError().setError(DBError::UnknownError); + aMessage.Write(1, LastErrorCode()); + delete interfaceBuf; + return ret; + } + + QByteArray interfaceDescriptorByteArray((const char*)ptrToBuf.Ptr(), ptrToBuf.Length()); + QDataStream out(interfaceDescriptorByteArray); + QServiceInterfaceDescriptor interfaceDescriptor; + out >> interfaceDescriptor; + + iDb->setInterfaceDefault(interfaceDescriptor); + aMessage.Write(1, LastErrorCode()); + delete interfaceBuf; + + return ret; + } + +void CDatabaseManagerServerSession::SetChangeNotificationsEnabled(const RMessage2& aMessage) + { + + m_watcher->addPath(iDb->databasePath()); + + if (aMessage.Int0() == 1) // 1 == Notifications enabled + { + m_knownServices.clear(); + m_knownServices = iDb->getServiceNames(QString()); + } + else + { + m_watcher->removePath(iDb->databasePath()); + m_knownServices.clear(); + } + + aMessage.Write(1, LastErrorCode()); + } + +void CDatabaseManagerServerSession::databaseChanged(const QString &path) + { + QStringList currentServices = iDb->getServiceNames(QString()); + + if (currentServices == m_knownServices) + return; + + QStringList newServices; + for (int i=0; i<currentServices.count(); i++) { + if (!m_knownServices.contains(currentServices[i])) + newServices << currentServices[i]; + } + + QStringList removedServices; + for (int i=0; i<m_knownServices.count(); i++) { + if (!currentServices.contains(m_knownServices[i])) + removedServices << m_knownServices[i]; + } + + m_knownServices = currentServices; + for (int i=0; i<newServices.count(); i++) + ServiceAdded(newServices[i]); + for (int i=0; i<removedServices.count(); i++) + ServiceRemoved(removedServices[i]); + } + +TError CDatabaseManagerServerSession::LastErrorCode() + { + return TError(iDb->lastError().code()); + } + +void CDatabaseManagerServerSession::ServiceAdded(const QString& aServiceName) + { + if (iWaitingAsyncRequest) + { + TPckgBuf<TInt> state(0); + TPtrC str(reinterpret_cast<const TUint16*>(aServiceName.utf16())); + iMsg.Write(0, str); + iMsg.Write(1, state); + iMsg.Write(2, LastErrorCode()); + iMsg.Complete(ENotifySignalComplete); + iWaitingAsyncRequest = EFalse; + } + } + +void CDatabaseManagerServerSession::ServiceRemoved(const QString& aServiceName) + { + if (iWaitingAsyncRequest) + { + TPckgBuf<TInt> state(1); + TPtrC str(reinterpret_cast<const TUint16*>(aServiceName.utf16())); + iMsg.Write(0, str); + iMsg.Write(1, state); + iMsg.Write(2, LastErrorCode()); + iMsg.Complete(ENotifySignalComplete); + iWaitingAsyncRequest = EFalse; + } + } + +bool CDatabaseManagerServerSession::openDb() + { + if (iDb) { + if (iDb->isOpen()) { + return true; + } else { + return iDb->open(); + } + } + + return false; + } + +QTM_END_NAMESPACE + +// End of File diff --git a/src/serviceframework/databasemanagerserver_symbian/databasemanagersession_p.h b/src/serviceframework/databasemanagerserver_symbian/databasemanagersession_p.h new file mode 100644 index 00000000..abcc06ec --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/databasemanagersession_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CDATABASEMANAGERSESSION_H_ +#define CDATABASEMANAGERSESSION_H_ + +#include <qserviceframeworkglobal.h> +#include <e32base.h> +#include <QObject> +#include <QStringList> + +class QFileSystemWatcher; + +QTM_BEGIN_NAMESPACE + +class CDatabaseManagerServer; +class DatabaseManagerSignalHandler; +class ServiceDatabase; + +typedef TPckgBuf<TInt> TError; + +class CDatabaseManagerServerSession : public CSession2 + { + public: + static CDatabaseManagerServerSession* NewL(CDatabaseManagerServer& aServer, QString dbPath); + static CDatabaseManagerServerSession* NewLC(CDatabaseManagerServer& aServer, QString dbPath); + virtual ~CDatabaseManagerServerSession(); + + void ServiceL(const RMessage2& aMessage); + void DispatchMessageL(const RMessage2& aMessage); + + TInt RegisterServiceL(const RMessage2& aMessage); + TInt UnregisterServiceL(const RMessage2& aMessage); + TInt ServiceInitializedL(const RMessage2& aMessage); + TInt InterfacesL(const RMessage2& aMessage); + TInt ServiceNamesL(const RMessage2& aMessage); + TInt InterfaceDefaultL(const RMessage2& aMessage); + TInt SetInterfaceDefaultL(const RMessage2& aMessage); + TInt SetInterfaceDefault2L(const RMessage2& aMessage); + void NotifyServiceSignal(const RMessage2& aMessage); + TInt CancelNotifyServiceSignal(const RMessage2& aMessage); + void SetChangeNotificationsEnabled(const RMessage2& aMessage); + TInt InterfaceDefaultSize(const RMessage2& aMessage); + TInt InterfacesSizeL(const RMessage2& aMessage); + TInt ServiceNamesSizeL(const RMessage2& aMessage); + + void ServiceRemoved(const QString& aServiceName); + void ServiceAdded(const QString& aServiceName); + void databaseChanged(const QString &path); + + private: + CDatabaseManagerServerSession(CDatabaseManagerServer& aServer); + void ConstructL(QString dbPath); + TError LastErrorCode(); + bool openDb(); + + protected: + void PanicClient(const RMessage2& aMessage, TInt aPanic) const; + + private: + CDatabaseManagerServer& iServer; + QByteArray* iByteArray; + TBool iWaitingAsyncRequest; + RMessage2 iMsg; + DatabaseManagerSignalHandler* iDatabaseManagerSignalHandler; + ServiceDatabase *iDb; + QStringList m_knownServices; + QFileSystemWatcher *m_watcher; + }; + +QTM_END_NAMESPACE + +#endif + +// End of File diff --git a/src/serviceframework/databasemanagerserver_symbian/databasemanagersignalhandler.cpp b/src/serviceframework/databasemanagerserver_symbian/databasemanagersignalhandler.cpp new file mode 100644 index 00000000..830fb450 --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/databasemanagersignalhandler.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "databasemanagersignalhandler_p.h" + +QTM_BEGIN_NAMESPACE + +DatabaseManagerSignalHandler::DatabaseManagerSignalHandler(CDatabaseManagerServerSession& databaseManagerServerSession) + : iDatabaseManagerServerSession(databaseManagerServerSession) +{ +} + +void DatabaseManagerSignalHandler::databaseChanged(const QString &path) +{ + iDatabaseManagerServerSession.databaseChanged(path); +} + +#include "moc_databasemanagersignalhandler_p.cpp" +QTM_END_NAMESPACE diff --git a/src/serviceframework/databasemanagerserver_symbian/databasemanagersignalhandler_p.h b/src/serviceframework/databasemanagerserver_symbian/databasemanagersignalhandler_p.h new file mode 100644 index 00000000..4345d76c --- /dev/null +++ b/src/serviceframework/databasemanagerserver_symbian/databasemanagersignalhandler_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef DATABASEMANAGERSIGNALHANDLER_H +#define DATABASEMANAGERSIGNALHANDLER_H + +#include <QObject> +#include "databasemanagersession_p.h" + +QTM_BEGIN_NAMESPACE + +class DatabaseManagerSignalHandler : public QObject + { + Q_OBJECT + + public: + DatabaseManagerSignalHandler(CDatabaseManagerServerSession& databaseManagerServerSession); + + public Q_SLOTS: + void databaseChanged(const QString &path); + + public: + CDatabaseManagerServerSession& iDatabaseManagerServerSession; + }; + +QTM_END_NAMESPACE + +#endif + +// End of File + diff --git a/src/serviceframework/dberror.cpp b/src/serviceframework/dberror.cpp new file mode 100644 index 00000000..c9691a03 --- /dev/null +++ b/src/serviceframework/dberror.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "dberror_p.h" + +QTM_BEGIN_NAMESPACE + +DBError::DBError() +{ + setError(NoError); +} + +void DBError::setError(ErrorCode error, const QString &text) +{ + m_error = error; + switch (error) { + case (NoError): + m_text = QLatin1String("No error"); + break; + case(DatabaseNotOpen): + m_text = QLatin1String("Database not open"); + break; + case(InvalidDatabaseConnection): + m_text = QLatin1String("Invalid database connection"); + break; + case(ExternalIfaceIDFound): + m_text = QLatin1String("External InterfaceID found"); + break; + case(SqlError): + case(NotFound): + case(LocationAlreadyRegistered): + case(IfaceImplAlreadyRegistered): + case(CannotCreateDbDir): + case(InvalidDescriptorScope): + case(IfaceIDNotExternal): + case(InvalidDatabaseFile): + case(NoWritePermissions): + case(CannotOpenServiceDb): + m_text = text; + break; + default: + m_text= QLatin1String("Unknown error"); + m_error = UnknownError; + } +} +QTM_END_NAMESPACE diff --git a/src/serviceframework/dberror_p.h b/src/serviceframework/dberror_p.h new file mode 100644 index 00000000..1a02a05d --- /dev/null +++ b/src/serviceframework/dberror_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DBERROR_H +#define DBERROR_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qserviceframeworkglobal.h" +#include <QString> + +QTM_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT DBError +{ + public: + enum ErrorCode { + NoError, + DatabaseNotOpen = -2000, //A connection with the database has not been opened + // database needs to be opened before any operations take place + InvalidDatabaseConnection, //The database connection does not have a valid driver + LocationAlreadyRegistered, //A service location has already been registered. + IfaceImplAlreadyRegistered, //An interface implementation by a given service is already registered to that service + NotFound, + SqlError, //An Sql error occurred. + IfaceIDNotExternal, //InterfaceID does not refer to an external interface implementation + CannotCreateDbDir, //Directory to contain database could not be created(usu a permissions issue) + CannotOpenServiceDb, //service database cannot be opened(usually a permissions issue) + ExternalIfaceIDFound, //Notification for interfaceDefault() on a user scope database + // to indicate that a default refers to an interface implementation in the + // system scope database + InvalidDescriptorScope, //Notification for setInterfaceDefault() on a system scope database + // to indicate that a user scope descriptor cannot be used + // with a system scope database. + InvalidDatabaseFile, //database file is corrupted or not a valid database + NoWritePermissions, //trying to perform a write operation without sufficient permissions + UnknownError + }; + DBError(); + void setError(ErrorCode error, const QString &errorText = QString()); + void setSQLError(const QString &errorText) { + m_error = SqlError; + m_text = errorText; + } + void setNotFoundError(const QString &errorText) { + m_error = NotFound; + m_text = errorText; + } + QString text() const { return m_text; } + ErrorCode code() const { return m_error; } + private: + QString m_text; + ErrorCode m_error; +}; +QTM_END_NAMESPACE + +#endif //DBERROR_H diff --git a/src/serviceframework/ipc/instancemanager.cpp b/src/serviceframework/ipc/instancemanager.cpp new file mode 100644 index 00000000..549a00d5 --- /dev/null +++ b/src/serviceframework/ipc/instancemanager.cpp @@ -0,0 +1,247 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qserviceframeworkglobal.h> +#include <QTimer> +#include "instancemanager_p.h" +#include "qremoteserviceregisterentry_p.h" + +QTM_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(InstanceManager, typeRegister); + +/*! + \internal + + Returns the instance manager for the service process + \since 1.1 +*/ +InstanceManager* InstanceManager::instance() +{ + return typeRegister(); +} + +InstanceManager::InstanceManager(QObject *parent) + : QObject(parent) +{ +} + +InstanceManager::~InstanceManager() +{ + QList<QRemoteServiceRegister::Entry> allEntries = metaMap.keys(); + while (!allEntries.isEmpty()) { + ServiceIdentDescriptor descr = metaMap.take(allEntries.takeFirst()); + if (descr.entryData->instanceType == QRemoteServiceRegister::GlobalInstance) { + if (descr.globalInstance) + QTimer::singleShot(0, descr.globalInstance, SLOT(deleteLater())); // Symbian issue, use timer + descr.globalInstance = 0; + } else { + QList<QUuid> allUuids = descr.individualInstances.keys(); + while (!allUuids.isEmpty()) { + QTimer::singleShot(0, descr.individualInstances.take(allUuids.takeFirst()), SLOT(deleteLater())); // Symbian issue + } + } + } + +} + +/*! + \internal + + Adds an entry to the map of service identifiers + \since 1.1 +*/ +bool InstanceManager::addType(const QRemoteServiceRegister::Entry& e) +{ + QMutexLocker ml(&lock); + + if (metaMap.contains(e)) { + qWarning() << "Service" << e.serviceName() << "(" << e.interfaceName() + << ", " << e.version() << ")" << "already registered"; + } else { + ServiceIdentDescriptor d; + d.entryData = e.d; + metaMap.insert(e, d); + return true; + } + return false; +} + +/*! + \internal + + Returns the metaobject of a registered service object identified by its \a entry + \since 1.1 +*/ +const QMetaObject* InstanceManager::metaObject(const QRemoteServiceRegister::Entry& entry) const +{ + QMutexLocker ml(&lock); + if (metaMap.contains(entry)) { + return metaMap[entry].entryData->meta; + } else { + return 0; + } +} + +/*! + \internal + + Returns a list of all the registered entries + \since 1.1 +*/ +QList<QRemoteServiceRegister::Entry> InstanceManager::allEntries() const +{ + QMutexLocker ml(&lock); + return metaMap.keys(); +} + +/*! + \internal + + Instance manager takes ownership of service instance. Returns a null pointer + if \a entry cannot be mapped to a known meta object. The \a instanceId will + contain the unique ID for the new service instance. + \since 1.1 +*/ +QObject* InstanceManager::createObjectInstance(const QRemoteServiceRegister::Entry& entry, QUuid& instanceId) +{ + instanceId = QUuid(); + QMutexLocker ml(&lock); + if (!metaMap.contains(entry)) + return 0; + + QObject* service = 0; + ServiceIdentDescriptor& descr = metaMap[entry]; + + if (descr.entryData->instanceType == QRemoteServiceRegister::GlobalInstance) { + if (descr.globalInstance) { + service = descr.globalInstance; + instanceId = descr.globalId; + descr.globalRefCount++; + } else { + service = (*descr.entryData->cptr)(); + if (!service) + return 0; + + descr.globalInstance = service; + descr.globalId = instanceId = QUuid::createUuid(); + descr.globalRefCount = 1; + } + } else { + service = (*descr.entryData->cptr)(); + if (!service) + return 0; + instanceId = QUuid::createUuid(); + descr.individualInstances.insert(instanceId, service); + } + + return service; +} + +/*! + \internal + + The associated service object instance will be deleted in the service process. + Removes an instance with \a instanceId from a map of remote service descriptors + using the \a entry as the key. + + Emits instanceClosed() and allInstancesClosed() if no more instances are open + \since 1.1 +*/ +void InstanceManager::removeObjectInstance(const QRemoteServiceRegister::Entry& entry, const QUuid& instanceId) +{ + QMutexLocker ml(&lock); + if (!metaMap.contains(entry)) + return; + + ServiceIdentDescriptor& descr = metaMap[entry]; + if (descr.entryData->instanceType == QRemoteServiceRegister::GlobalInstance) { + if (descr.globalRefCount < 1) + return; + + if (descr.globalRefCount == 1) { + if (descr.globalInstance) + QTimer::singleShot(0, descr.globalInstance, SLOT(deleteLater())); + descr.globalInstance = 0; + descr.globalId = QUuid(); + descr.globalRefCount = 0; + emit instanceClosed(entry); + emit instanceClosed(entry, instanceId); //internal use + } else { + descr.globalRefCount--; + } + } else { + QObject* service = descr.individualInstances.take(instanceId); + if (service) { + QTimer::singleShot(0, service, SLOT(deleteLater())); // symbian issue + emit instanceClosed(entry); + emit instanceClosed(entry, instanceId); //internal use + } + } + + // Check that no instances are open + if (totalInstances() < 1) + emit allInstancesClosed(); +} + +/*! + \internal + + Provides a count of how many global and private instances are currently open + \since 1.1 +*/ +int InstanceManager::totalInstances() const +{ + int total = 0; + + QList<QRemoteServiceRegister::Entry> allEntries = metaMap.keys(); + foreach (const QRemoteServiceRegister::Entry& entry, allEntries) { + ServiceIdentDescriptor descr = metaMap[entry]; + total += descr.globalRefCount; + total += descr.individualInstances.size(); + } + + return total; +} + +#include "moc_instancemanager_p.cpp" + +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/instancemanager_p.h b/src/serviceframework/ipc/instancemanager_p.h new file mode 100644 index 00000000..a27efbc2 --- /dev/null +++ b/src/serviceframework/ipc/instancemanager_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICE_INSTANCE_MANAGER +#define QSERVICE_INSTANCE_MANAGER + +#include <qserviceframeworkglobal.h> +#include "qremoteserviceregister.h" +#include "qremoteserviceregisterentry_p.h" +#include <QHash> +#include <QMutexLocker> +#include <QMetaObject> +#include <QMetaClassInfo> +#include <QUuid> +#include <QDebug> + +QTM_BEGIN_NAMESPACE + +struct ServiceIdentDescriptor +{ + ServiceIdentDescriptor() : globalInstance(0), globalRefCount(0) + { + } + + QExplicitlySharedDataPointer<QRemoteServiceRegisterEntryPrivate> entryData; + + QHash<QUuid, QObject*> individualInstances; + QObject* globalInstance; + QUuid globalId; + int globalRefCount; +}; + +class Q_AUTOTEST_EXPORT InstanceManager : public QObject +{ + Q_OBJECT +public: + InstanceManager(QObject *parent = 0); + ~InstanceManager(); + + bool addType(const QRemoteServiceRegister::Entry& entry); + + const QMetaObject* metaObject(const QRemoteServiceRegister::Entry& ident) const; + QList<QRemoteServiceRegister::Entry> allEntries() const; + + int totalInstances() const; + + QObject* createObjectInstance(const QRemoteServiceRegister::Entry& entry, QUuid& instanceId); + void removeObjectInstance(const QRemoteServiceRegister::Entry& entry, const QUuid& instanceId); + + static InstanceManager* instance(); + +Q_SIGNALS: + void allInstancesClosed(); + void instanceClosed(const QRemoteServiceRegister::Entry&); + void instanceClosed(const QRemoteServiceRegister::Entry&, const QUuid&); + +private: + mutable QMutex lock; + QHash<QRemoteServiceRegister::Entry, ServiceIdentDescriptor> metaMap; +}; + + + +QTM_END_NAMESPACE + + +#endif //QSERVICE_INSTANCE_MANAGER diff --git a/src/serviceframework/ipc/ipc.pri b/src/serviceframework/ipc/ipc.pri new file mode 100644 index 00000000..58d63307 --- /dev/null +++ b/src/serviceframework/ipc/ipc.pri @@ -0,0 +1,46 @@ +INCLUDEPATH += ipc +symbian { + PRIVATE_HEADERS += ipc/qremoteserviceregister_s60_p.h \ + ipc/objectendpoint_p.h + SOURCES += ipc/qremoteserviceregister_s60.cpp \ + ipc/objectendpoint.cpp +} +else { + contains(QT_CONFIG,dbus) { + QT += dbus \ + network + PRIVATE_HEADERS += ipc/qremoteserviceregister_dbus_p.h \ + ipc/objectendpoint_dbus_p.h \ + ipc/qservicemetaobject_dbus_p.h + SOURCES += ipc/qremoteserviceregister_dbus_p.cpp \ + ipc/objectendpoint_dbus.cpp \ + ipc/qservicemetaobject_dbus.cpp + } + else { + QT += network + PRIVATE_HEADERS += ipc/qremoteserviceregister_ls_p.h \ + ipc/objectendpoint_p.h + SOURCES += ipc/qremoteserviceregister_ls_p.cpp \ + ipc/objectendpoint.cpp + } +} + +PRIVATE_HEADERS += ipc/qslotinvoker_p.h \ + ipc/qsignalintercepter_p.h \ + ipc/instancemanager_p.h \ + ipc/qservicepackage_p.h \ + ipc/proxyobject_p.h \ + ipc/ipcendpoint_p.h \ + ipc/qremoteserviceregister_p.h \ + ipc/qremoteserviceregisterentry_p.h \ + ipc/qmetaobjectbuilder_p.h + +SOURCES += ipc/qslotinvoker.cpp \ + ipc/qsignalintercepter.cpp \ + ipc/instancemanager.cpp \ + ipc/qservicepackage.cpp \ + ipc/proxyobject.cpp \ + ipc/ipcendpoint.cpp \ + ipc/qremoteserviceregister_p.cpp \ + ipc/qmetaobjectbuilder.cpp + diff --git a/src/serviceframework/ipc/ipcendpoint.cpp b/src/serviceframework/ipc/ipcendpoint.cpp new file mode 100644 index 00000000..43578455 --- /dev/null +++ b/src/serviceframework/ipc/ipcendpoint.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "ipcendpoint_p.h" + +QTM_BEGIN_NAMESPACE +/*! + QServiceIpcEndPoint +*/ +QServiceIpcEndPoint::QServiceIpcEndPoint(QObject* parent) + : QObject( parent ) +{ +} + +QServiceIpcEndPoint::~QServiceIpcEndPoint() +{ + incoming.clear(); +} + +bool QServiceIpcEndPoint::packageAvailable() const +{ + return !incoming.isEmpty(); +} + +QServicePackage QServiceIpcEndPoint::nextPackage() +{ + if (!incoming.isEmpty()) + return incoming.dequeue(); + return QServicePackage(); +} + +void QServiceIpcEndPoint::writePackage(QServicePackage newPackage) +{ + flushPackage(newPackage); +} + +#include "moc_ipcendpoint_p.cpp" +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/ipcendpoint_p.h b/src/serviceframework/ipc/ipcendpoint_p.h new file mode 100644 index 00000000..faf445ab --- /dev/null +++ b/src/serviceframework/ipc/ipcendpoint_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q_SERVICECOMM_P_H +#define Q_SERVICECOMM_P_H + +#include "qserviceframeworkglobal.h" +#include <QObject> +#include <QQueue> + +#include "qservicepackage_p.h" + +QTM_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QServiceIpcEndPoint : public QObject +{ + Q_OBJECT +public: + QServiceIpcEndPoint(QObject* object = 0); + virtual ~QServiceIpcEndPoint(); + + bool packageAvailable() const; + QServicePackage nextPackage(); + + void writePackage(QServicePackage newPackage); + +Q_SIGNALS: + void readyRead(); + void disconnected(); + +protected: + virtual void flushPackage(const QServicePackage& out) = 0; + + QQueue<QServicePackage> incoming; +}; + + +QTM_END_NAMESPACE + +#endif //Q_SERVICECOMM_P_H diff --git a/src/serviceframework/ipc/metaobjectbuilder.pri b/src/serviceframework/ipc/metaobjectbuilder.pri new file mode 100644 index 00000000..561bddb5 --- /dev/null +++ b/src/serviceframework/ipc/metaobjectbuilder.pri @@ -0,0 +1,12 @@ +#check version for 4.7 ... +contains(QT_MAJOR_VERSION, 4):lessThan(QT_MINOR_VERSION, 8) { + OBJECTBUILDER_INCLUDEPATH += ipc/metaobjectbuilder47 + OBJECTBUILDER_DEPENDPATH += ipc/metaobjectbuilder47 + OBJECTBUILDER_HEADERS += ipc/metaobjectbuilder47/qmetaobjectbuilder_p.h + OBJECTBUILDER_SOURCES += ipc/metaobjectbuilder47/qmetaobjectbuilder.cpp +} else { + OBJECTBUILDER_INCLUDEPATH += ipc/metaobjectbuilder + OBJECTBUILDER_DEPENDPATH += ipc/metaobjectbuilder + OBJECTBUILDER_HEADERS += ipc/metaobjectbuilder/qmetaobjectbuilder_p.h + OBJECTBUILDER_SOURCES += ipc/metaobjectbuilder/qmetaobjectbuilder.cpp +} diff --git a/src/serviceframework/ipc/objectendpoint.cpp b/src/serviceframework/ipc/objectendpoint.cpp new file mode 100644 index 00000000..3edd7984 --- /dev/null +++ b/src/serviceframework/ipc/objectendpoint.cpp @@ -0,0 +1,636 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "objectendpoint_p.h" +#include "instancemanager_p.h" +#include "qmetaobjectbuilder_p.h" +#include "proxyobject_p.h" +#include "qsignalintercepter_p.h" +#include <QTimer> +#include <QEventLoop> +#include <QEvent> +#include <QVarLengthArray> +#include <QTime> +#include <QCoreApplication> + +QTM_BEGIN_NAMESPACE + +class Response +{ +public: + Response() : isFinished(false), result(0) + { } + + bool isFinished; + void* result; +}; + +typedef QHash<QUuid, Response*> Replies; +Q_GLOBAL_STATIC(Replies, openRequests); + +class ServiceSignalIntercepter : public QSignalIntercepter +{ + //Do not put Q_OBJECT here +public: + ServiceSignalIntercepter(QObject* sender, const QByteArray& signal, + ObjectEndPoint* parent) + : QSignalIntercepter(sender, signal, parent), endPoint(parent) + { + + } + + void setMetaIndex(int index) + { + metaIndex = index; + } + +protected: + void activated( const QList<QVariant>& args ) + { + //qDebug() << signal() << "emitted." << args; + endPoint->invokeRemote(metaIndex, args, QMetaType::Void); + } +private: + ObjectEndPoint* endPoint; + int metaIndex; + +}; + +class ObjectEndPointPrivate +{ +public: + ObjectEndPointPrivate() + { + } + + ~ObjectEndPointPrivate() + { + } + + //service side + void setupSignalIntercepters(QObject * service) + { + Q_ASSERT(endPointType == ObjectEndPoint::Service); + + //create a signal intercepter for each signal + //offered by service + //exclude QObject signals + const QMetaObject* mo = service->metaObject(); + while (mo && strcmp(mo->className(), "QObject")) + { + for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) { + const QMetaMethod method = mo->method(i); + if (method.methodType() == QMetaMethod::Signal) { + QByteArray signal = method.signature(); + //add '2' for signal - see QSIGNAL_CODE + ServiceSignalIntercepter* intercept = + new ServiceSignalIntercepter(service, "2"+signal, parent ); + intercept->setMetaIndex(i); + } + } + mo = mo->superClass(); + } + } + + /*! + Activate slots connected to given signal. Unfortunately we can only do this + using the signal index relative to the meta object defining the signal. + */ + int triggerConnectedSlots(QObject* service, const QMetaObject* meta, int id, void **args) + { + Q_ASSERT(endPointType == ObjectEndPoint::Client); + + const QMetaObject* parentMeta = meta->superClass(); + if (parentMeta) + id = triggerConnectedSlots(service, parentMeta, id, args); + + if (id < 0) + return id; + + const int methodsThisType = meta->methodCount() - meta->methodOffset(); + if (id >= 0 && id < methodsThisType) + QMetaObject::activate(service, meta, id, args); + + id -= methodsThisType; + return id; + } + + //used on client and service side + ObjectEndPoint::Type endPointType; + ObjectEndPoint* parent; + + //used on service side + QRemoteServiceRegister::Entry entry; + QUuid serviceInstanceId; +}; + +ObjectEndPoint::ObjectEndPoint(Type type, QServiceIpcEndPoint* comm, QObject* parent) + : QObject(parent), dispatch(comm), service(0) +{ + Q_ASSERT(dispatch); + d = new ObjectEndPointPrivate; + d->parent = this; + d->endPointType = type; + + dispatch->setParent(this); + connect(dispatch, SIGNAL(readyRead()), this, SLOT(newPackageReady()), Qt::QueuedConnection); + connect(dispatch, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::QueuedConnection); + if (type == Client) { + return; //we are waiting for conctructProxy() call + } else { + if (dispatch->packageAvailable()) + QTimer::singleShot(0, this, SLOT(newPackageReady())); + } +} + +ObjectEndPoint::~ObjectEndPoint() +{ + delete d; +} + +void ObjectEndPoint::disconnected() +{ + if (d->endPointType == Service) { + InstanceManager::instance()->removeObjectInstance(d->entry, d->serviceInstanceId); + } + // deleteLater on symbian does not function properly from disconnect() + // maybe disconnect comes in on a thread? Call from timer works. + QTimer::singleShot(0, this, SLOT(deleteLater())); +} + +/* + Client requests proxy object. The proxy is owned by calling + code and this object must clean itself up upon destruction of + proxy. +*/ +QObject* ObjectEndPoint::constructProxy(const QRemoteServiceRegister::Entry & entry) +{ + //client side + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + //ask for serialized meta object + //get proxy based on meta object + //return meta object + QServicePackage p; + p.d = new QServicePackagePrivate(); + p.d->messageId = QUuid::createUuid(); + p.d->entry = entry; + + Response* response = new Response(); + openRequests()->insert(p.d->messageId, response); + + dispatch->writePackage(p); + waitForResponse(p.d->messageId); + + if (response->isFinished) { + if (response->result == 0) + qWarning() << "Request for remote service failed"; + else + service = reinterpret_cast<QServiceProxy* >(response->result); + } else { + qDebug() << "response passed but not finished"; + return 0; + } + + openRequests()->take(p.d->messageId); + delete response; + + return service; +} + +void ObjectEndPoint::newPackageReady() +{ + //client and service side + + while (dispatch->packageAvailable()) + { + QServicePackage p = dispatch->nextPackage(); + if (!p.isValid()) + continue; + + switch (p.d->packageType) { + case QServicePackage::ObjectCreation: + objectRequest(p); + break; + case QServicePackage::MethodCall: + methodCall(p); + break; + case QServicePackage::PropertyCall: + propertyCall(p); + break; + default: + qWarning() << "Unknown package type received."; + } + } +} + +void ObjectEndPoint::propertyCall(const QServicePackage& p) +{ + if (p.d->responseType == QServicePackage::NotAResponse) { + //service side + Q_ASSERT(d->endPointType == ObjectEndPoint::Service); + + QByteArray data = p.d->payload.toByteArray(); + QDataStream stream(&data, QIODevice::ReadOnly); + int metaIndex = -1; + QVariant arg; + int callType; + stream >> metaIndex; + stream >> arg; + stream >> callType; + const QMetaObject::Call c = (QMetaObject::Call) callType; + + QVariant result; + QMetaProperty property = service->metaObject()->property(metaIndex); + if (property.isValid()) { + switch (c) { + case QMetaObject::ReadProperty: + result = property.read(service); + break; + case QMetaObject::WriteProperty: + property.write(service, arg); + break; + case QMetaObject::ResetProperty: + property.reset(service); + break; + default: + break; + + } + } + + if (c == QMetaObject::ReadProperty) { + QServicePackage response = p.createResponse(); + if (property.isValid()) { + response.d->responseType = QServicePackage::Success; + response.d->payload = result; + } else { + response.d->responseType = QServicePackage::Failed; + } + dispatch->writePackage(response); + } + } else { + //client side + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + Response* response = openRequests()->value(p.d->messageId); + response->isFinished = true; + if (p.d->responseType == QServicePackage::Failed) { + response->result = 0; + QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished())); + qWarning() << "Service method call failed"; + return; + } + QVariant* variant = new QVariant(p.d->payload); + response->result = reinterpret_cast<void *>(variant); + + QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished())); + } +} + +void ObjectEndPoint::objectRequest(const QServicePackage& p) +{ + if (p.d->responseType != QServicePackage::NotAResponse ) { + //client side + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + Response* response = openRequests()->value(p.d->messageId); + if (p.d->responseType == QServicePackage::Failed) { + response->result = 0; + response->isFinished = true; + QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished())); + qWarning() << "Service instantiation failed"; + return; + } + //deserialize meta object and + //create proxy object + QServiceProxy* proxy = new QServiceProxy(p.d->payload.toByteArray(), this); + response->result = reinterpret_cast<void *>(proxy); + response->isFinished = true; + + //wake up waiting code + QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished())); + + } else { + //service side + Q_ASSERT(d->endPointType == ObjectEndPoint::Service); + + QServicePackage response = p.createResponse(); + InstanceManager* m = InstanceManager::instance(); + + //get meta object from type register + const QMetaObject* meta = m->metaObject(p.d->entry); + if (!meta) { + qDebug() << "Unknown type" << p.d->entry; + dispatch->writePackage(response); + return; + } + + //serialize meta object + QByteArray data; + QDataStream stream( &data, QIODevice::WriteOnly | QIODevice::Append ); + QMetaObjectBuilder builder(meta); + builder.serialize(stream); + + //instantiate service object from type register + service = m->createObjectInstance(p.d->entry, d->serviceInstanceId); + if (!service) { + qWarning() << "Cannot instanciate service object"; + dispatch->writePackage(response); + return; + } + d->setupSignalIntercepters(service); + + //send meta object + d->entry = p.d->entry; + response.d->entry = p.d->entry; + response.d->responseType = QServicePackage::Success; + response.d->payload = QVariant(data); + dispatch->writePackage(response); + } +} + +void ObjectEndPoint::methodCall(const QServicePackage& p) +{ + if (p.d->responseType == QServicePackage::NotAResponse ) { + //service side if slot invocation + //client side if signal emission (isSignal==true) + + QByteArray data = p.d->payload.toByteArray(); + QDataStream stream(&data, QIODevice::ReadOnly); + int metaIndex = -1; + QVariantList args; + stream >> metaIndex; + stream >> args; + + QMetaMethod method = service->metaObject()->method(metaIndex); + const bool isSignal = (method.methodType() == QMetaMethod::Signal); + const int returnType = QMetaType::type(method.typeName()); + + if (isSignal) { + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + // Construct the raw argument list. + /* we ignore a possible return type of the signal. The value is + not deterministic and it can actually create memory leaks + in moc generated code. + */ + const int numArgs = args.size(); + QVarLengthArray<void *, 32> a( numArgs+1 ); + a[0] = 0; + + const QList<QByteArray> pTypes = method.parameterTypes(); + for ( int arg = 0; arg < numArgs; ++arg ) { + if (pTypes.at(arg) == "QVariant") + a[arg+1] = (void *)&( args[arg] ); + else + a[arg+1] = (void *)( args[arg].data() ); + } + + d->triggerConnectedSlots(service, service->metaObject(), metaIndex, a.data()); + return; + } + //service side + Q_ASSERT(d->endPointType == ObjectEndPoint::Service); + + const char* typenames[] = {0,0,0,0,0,0,0,0,0,0}; + const void* param[] = {0,0,0,0,0,0,0,0,0,0}; + + for (int i=0; i<args.size(); i++) { + if (args[i].isValid()) { + typenames[i] = args[i].typeName(); + } else { + if (method.parameterTypes().at(i) == "QVariant") + typenames[i] = "QVariant"; + } + param[i] = args[i].constData(); + } + + + bool result = false; + if (returnType == QMetaType::Void && strcmp(method.typeName(), "QVariant")) { + result = method.invoke(service, + QGenericArgument(typenames[0], param[0]), + QGenericArgument(typenames[1], param[1]), + QGenericArgument(typenames[2], param[2]), + QGenericArgument(typenames[3], param[3]), + QGenericArgument(typenames[4], param[4]), + QGenericArgument(typenames[5], param[5]), + QGenericArgument(typenames[6], param[6]), + QGenericArgument(typenames[7], param[7]), + QGenericArgument(typenames[8], param[8]), + QGenericArgument(typenames[9], param[9])); + } else { + //result buffer + QVariant returnValue; + //ignore whether QVariant is a declared meta type or not + if (returnType != QVariant::Invalid && strcmp(method.typeName(), "QVariant")) { + returnValue = QVariant(returnType, (const void*) 0); + } + + QGenericReturnArgument ret(method.typeName(), returnValue.data()); + result = method.invoke(service, ret, + QGenericArgument(typenames[0], param[0]), + QGenericArgument(typenames[1], param[1]), + QGenericArgument(typenames[2], param[2]), + QGenericArgument(typenames[3], param[3]), + QGenericArgument(typenames[4], param[4]), + QGenericArgument(typenames[5], param[5]), + QGenericArgument(typenames[6], param[6]), + QGenericArgument(typenames[7], param[7]), + QGenericArgument(typenames[8], param[8]), + QGenericArgument(typenames[9], param[9])); + + QServicePackage response = p.createResponse(); + + if (result) { + response.d->responseType = QServicePackage::Success; + response.d->payload = returnValue; + } else { + response.d->responseType = QServicePackage::Failed; + } + dispatch->writePackage(response); + + } + if (!result) + qWarning( "%s::%s cannot be called.", service->metaObject()->className(), method.signature()); + } else { + //client side + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + Response* response = openRequests()->value(p.d->messageId); + if (response){ + response->isFinished = true; + if (p.d->responseType == QServicePackage::Failed) { + response->result = 0; + QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished())); + return; + } + QVariant* variant = new QVariant(p.d->payload); + response->result = reinterpret_cast<void *>(variant); + + QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished())); + } + } +} + +/* + Will block if return value expected + Handles property calls +*/ +QVariant ObjectEndPoint::invokeRemoteProperty(int metaIndex, const QVariant& arg, int /*returnType*/, QMetaObject::Call c ) +{ + //client and service side + Q_ASSERT(d->endPointType == ObjectEndPoint::Client + || d->endPointType == ObjectEndPoint::Service); + + QServicePackage p; + p.d = new QServicePackagePrivate(); + p.d->packageType = QServicePackage::PropertyCall; + p.d->messageId = QUuid::createUuid(); + + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly|QIODevice::Append); + stream << metaIndex << arg << c; + p.d->payload = data; + + if (c == QMetaObject::ReadProperty) { + //create response and block for answer + Response* response = new Response(); + openRequests()->insert(p.d->messageId, response); + + dispatch->writePackage(p); + waitForResponse(p.d->messageId); + + QVariant result; + if (response->isFinished) { + if (response->result == 0) { + qWarning() << "Service property call failed"; + } else { + QVariant* resultPointer = reinterpret_cast<QVariant* >(response->result); + result = (*resultPointer); + delete resultPointer; + } + } else { + qDebug() << "response passed but not finished"; + } + + openRequests()->take(p.d->messageId); + delete response; + + return result; + } else { + dispatch->writePackage(p); + } + + return QVariant(); +} + +/* + Will block if return value expected + Handles signal/slots +*/ +QVariant ObjectEndPoint::invokeRemote(int metaIndex, const QVariantList& args, int returnType) +{ + //client side + //Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + QServicePackage p; + p.d = new QServicePackagePrivate(); + p.d->packageType = QServicePackage::MethodCall; + p.d->messageId = QUuid::createUuid(); + + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly|QIODevice::Append); + stream << metaIndex << args; + p.d->payload = data; + + if (returnType == QMetaType::Void) { + dispatch->writePackage(p); + } else { + //create response and block for answer + Response* response = new Response(); + openRequests()->insert(p.d->messageId, response); + + dispatch->writePackage(p); + waitForResponse(p.d->messageId); + + QVariant result; + if (response->isFinished) { + if (response->result == 0) { + qWarning() << "Remote function call failed"; + } else { + QVariant* resultPointer = reinterpret_cast<QVariant* >(response->result); + result = (*resultPointer); + delete resultPointer; + } + } else { + qDebug() << "response passed but not finished"; + } + + openRequests()->take(p.d->messageId); + delete response; + + return result; + + } + + return QVariant(); +} + +void ObjectEndPoint::waitForResponse(const QUuid& requestId) +{ + qDebug() << "start ObjectEndPoint::waitForResponse"; + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + if (openRequests()->contains(requestId) ) { + Response* response = openRequests()->value(requestId); + QEventLoop* loop = new QEventLoop( this ); + QTimer::singleShot(30000, loop, SLOT(quit())); + connect(this, SIGNAL(pendingRequestFinished()), loop, SLOT(quit())); + loop->exec(); + delete loop; + qDebug() << "- response->isFinished: " << response->isFinished; + + } + qDebug() << "finished ObjectEndPoint::waitForResponse"; +} + +#include "moc_objectendpoint_p.cpp" + +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/objectendpoint_dbus.cpp b/src/serviceframework/ipc/objectendpoint_dbus.cpp new file mode 100644 index 00000000..ce03e864 --- /dev/null +++ b/src/serviceframework/ipc/objectendpoint_dbus.cpp @@ -0,0 +1,728 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "objectendpoint_dbus_p.h" +#include "instancemanager_p.h" +#include "qmetaobjectbuilder_p.h" +#include "proxyobject_p.h" +#include "qsignalintercepter_p.h" +#include <QTimer> +#include <QEventLoop> +#include <QVarLengthArray> + +QTM_BEGIN_NAMESPACE + +class Response +{ +public: + Response() : isFinished(false), result(0) + { } + + bool isFinished; + void* result; +}; + +typedef QHash<QUuid, Response*> Replies; +Q_GLOBAL_STATIC(Replies, openRequests); + +class ServiceSignalIntercepter : public QSignalIntercepter +{ + //Do not put Q_OBJECT here +public: + ServiceSignalIntercepter(QObject* sender, const QByteArray& signal, + ObjectEndPoint* parent) + : QSignalIntercepter(sender, signal, parent), endPoint(parent) + { + + } + + void setMetaIndex(int index) + { + metaIndex = index; + } + +protected: + void activated( const QList<QVariant>& args ) + { + endPoint->invokeRemote(metaIndex, args, QMetaType::Void); + } +private: + ObjectEndPoint* endPoint; + int metaIndex; +}; + +struct ClientInstance { + QString clientId; + QRemoteServiceRegister::Entry entry; + QUuid instanceId; + int ref; +}; + +class ObjectEndPointPrivate +{ +public: + ObjectEndPointPrivate() + { + } + + ~ObjectEndPointPrivate() + { + } + + // Used on client and service side + ObjectEndPoint::Type endPointType; + ObjectEndPoint* parent; + + // Used to calculate the registered paths on DBus + QRemoteServiceRegister::Entry entry; + QUuid serviceInstanceId; + + // Service side local client ownership list + QList<ClientInstance> clientList; +}; + +/*! + Client to service communication only used for establishing an object request since the returned + proxy is an interface to that object registered on QtDBus. Client communicates directly to QtDBus + for method and property access. Signals are automatically relayed from QtDBus to the proxy object. +*/ +ObjectEndPoint::ObjectEndPoint(Type type, QServiceIpcEndPoint* comm, QObject* parent) + : QObject(parent), dispatch(comm), service(0), iface(0) +{ + Q_ASSERT(dispatch); + d = new ObjectEndPointPrivate; + d->parent = this; + d->endPointType = type; + + dispatch->setParent(this); + connect(dispatch, SIGNAL(readyRead()), this, SLOT(newPackageReady())); + if (type == Client) { + // client waiting for construct proxy and registers DBus custom type + qDBusRegisterMetaType<QTM_PREPEND_NAMESPACE(QServiceUserTypeDBus)>(); + qRegisterMetaType<QTM_PREPEND_NAMESPACE(QServiceUserTypeDBus)>(); + return; + } else { + connect(InstanceManager::instance(), + SIGNAL(instanceClosed(QRemoteServiceRegister::Entry,QUuid)), + this, SLOT(unregisterObjectDBus(QRemoteServiceRegister::Entry,QUuid))); + + if (dispatch->packageAvailable()) + QTimer::singleShot(0, this, SLOT(newPackageReady())); + } +} + +ObjectEndPoint::~ObjectEndPoint() +{ + if (iface) + delete iface; + delete d; +} + +/*! + Removes all instances of the client from the instance manager +*/ +void ObjectEndPoint::disconnected(const QString& clientId, const QString& instanceId) +{ + // Service Side + Q_ASSERT(d->endPointType != ObjectEndPoint::Client); + + for (int i=d->clientList.size()-1; i>=0; i--) { + // Find right client process + if (d->clientList[i].clientId == clientId) { + if (d->clientList[i].ref-- == 1) { + QRemoteServiceRegister::Entry entry = d->clientList[i].entry; + QUuid instance = d->clientList[i].instanceId; + + if (instance.toString() == instanceId) { + // Remove an instance from the InstanceManager and local list + InstanceManager::instance()->removeObjectInstance(entry, instance); + d->clientList.removeAt(i); + } + } + } + } +} + +/*! + Unregisters the DBus object +*/ +void ObjectEndPoint::unregisterObjectDBus(const QRemoteServiceRegister::Entry& entry, const QUuid& id) +{ + uint hash = qHash(id.toString()); + QString objPath = "/" + entry.interfaceName() + "/" + entry.version() + + "/" + QString::number(hash); + objPath.replace(QLatin1String("."), QLatin1String("/")); + QDBusConnection::sessionBus().unregisterObject(objPath, QDBusConnection::UnregisterTree); +} + +/*! + Client requests proxy object. The proxy is owned by calling + code and this object must clean itself up upon destruction of + proxy. +*/ +QObject* ObjectEndPoint::constructProxy(const QRemoteServiceRegister::Entry& entry) +{ + // Client side + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + // Request a serialized meta object + QServicePackage p; + p.d = new QServicePackagePrivate(); + p.d->messageId = QUuid::createUuid(); + p.d->entry = entry; + + Response* response = new Response(); + openRequests()->insert(p.d->messageId, response); + + dispatch->writePackage(p); + waitForResponse(p.d->messageId); + + // Get the proxy based on the meta object + if (response->isFinished) { + if (response->result == 0) + qWarning() << "Request for remote service failed"; + else + service = reinterpret_cast<QServiceProxy* >(response->result); + } else { + qDebug() << "response passed but not finished"; + } + + openRequests()->take(p.d->messageId); + delete response; + + if (!service) + return 0; + + // Connect all DBus interface signals to the proxy slots + const QMetaObject *mo = service->metaObject(); + while (mo && strcmp(mo->className(), "QObject")) { + for (int i = mo->methodOffset(); i < mo->methodCount(); i++) { + const QMetaMethod mm = mo->method(i); + if (mm.methodType() == QMetaMethod::Signal) { + QByteArray sig(mm.signature()); + + bool customType = false; + + QList<QByteArray> params = mm.parameterTypes(); + for (int arg = 0; arg < params.size(); arg++) { + const QByteArray& type = params[arg]; + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + sig.replace(QByteArray(type), QByteArray("QDBusVariant")); + customType = true; + } + } + + int serviceIndex = iface->metaObject()->indexOfSignal(sig); + QByteArray signal = QByteArray("2").append(sig); + + if (serviceIndex > 0) { + if (customType) { + QObject::connect(iface, signal.constData(), signalsObject, signal.constData()); + + ServiceSignalIntercepter *intercept = + new ServiceSignalIntercepter((QObject*)signalsObject, signal, this); + intercept->setMetaIndex(i); + } else { + QObject::connect(iface, signal.constData(), service, signal.constData()); + } + } + } + } + mo = mo->superClass(); + } + + return service; +} + +/*! + Received a new package from the DBus client-server controller. + Once an object request is handled there is only direct communication to the DBus object so + no other package types should be received on this layer. +*/ +void ObjectEndPoint::newPackageReady() +{ + // Client and service side + while (dispatch->packageAvailable()) + { + QServicePackage p = dispatch->nextPackage(); + if (!p.isValid()) + continue; + + if (p.d->packageType == QServicePackage::ObjectCreation) { + objectRequest(p); + } else { + qWarning() << "Unknown package type received."; + } + } +} + +/*! + Service finds existing objects or spawns new object instances and registers them on DBus using a + hash of the unique instance ID. This registered object has a special metaobject representation + of the service that is compatible with the QDBus type system. + + Client receives a package containing the information to connect an interface to the registered + DBus object. +*/ +void ObjectEndPoint::objectRequest(const QServicePackage& p) +{ + if (p.d->responseType != QServicePackage::NotAResponse ) { + // Client side + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + d->serviceInstanceId = p.d->instanceId; + d->entry = p.d->entry; + + Response* response = openRequests()->value(p.d->messageId); + if (p.d->responseType == QServicePackage::Failed) { + response->result = 0; + response->isFinished = true; + QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished())); + qWarning() << "Service instantiation failed"; + return; + } + + // Deserialize meta object and create proxy object + QServiceProxy* proxy = new QServiceProxy(p.d->payload.toByteArray(), this); + response->result = reinterpret_cast<void *>(proxy); + response->isFinished = true; + + // Create DBUS interface by using a hash of the service instance ID + QString serviceName = "com.nokia.qtmobility.sfw." + p.d->entry.serviceName(); + uint hash = qHash(d->serviceInstanceId.toString()); + QString objPath = "/" + p.d->entry.interfaceName() + "/" + p.d->entry.version() + "/" + QString::number(hash); + objPath.replace(QLatin1String("."), QLatin1String("/")); + +#ifdef DEBUG + qDebug() << "Client Interface ObjectPath:" << objPath; +#endif + // Instantiate our DBus interface and its corresponding signals object + if (!iface) + iface = new QDBusInterface(serviceName, objPath, QLatin1String(""), QDBusConnection::sessionBus(), this); + signalsObject = new QServiceMetaObjectDBus(iface, true); + + // Wake up waiting proxy construction code + QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished())); + + } else { + // Service side + Q_ASSERT(d->endPointType == ObjectEndPoint::Service); + + QServicePackage response = p.createResponse(); + InstanceManager* iManager = InstanceManager::instance(); + + // Instantiate service object from type register + service = iManager->createObjectInstance(p.d->entry, d->serviceInstanceId); + if (!service) { + qWarning() << "Cannot instantiate service object"; + dispatch->writePackage(response); + return; + } + + // Start DBus connection and register proxy service + if (!QDBusConnection::sessionBus().isConnected()) { + qWarning() << "Cannot connect to DBus"; + } + + // DBus registration path uses a hash of the service instance ID + QString serviceName = "com.nokia.qtmobility.sfw." + p.d->entry.serviceName(); + uint hash = qHash(d->serviceInstanceId.toString()); + QString objPath = "/" + p.d->entry.interfaceName() + "/" + p.d->entry.version() + "/" + QString::number(hash); + objPath.replace(QLatin1String("."), QLatin1String("/")); + + QServiceMetaObjectDBus *serviceDBus = new QServiceMetaObjectDBus(service); + QDBusConnection::sessionBus().registerObject(objPath, serviceDBus, QDBusConnection::ExportAllContents); + + QString clientId = p.d->payload.toString(); + + int exists = 0; + for (int i=d->clientList.size()-1; i>=0; i--) { + // Find right client process + if (d->clientList[i].clientId == clientId) { + d->clientList[i].ref++; + exists = 1; + break; + } + } + + if (!exists) { + // Add new instance to client ownership list + ClientInstance c; + c.clientId = clientId; + c.entry = p.d->entry; + c.instanceId = d->serviceInstanceId; + c.ref = 1; + d->clientList << c; + } + + + +#ifdef DEBUG + qDebug() << "Service Interface ObjectPath:" << objPath; + + const QMetaObject *s_meta = service->metaObject(); + qDebug() << "+++++++++++++++++++++SERVICE+++++++++++++++++++++++"; + qDebug() << s_meta->className(); + qDebug() << "METHOD COUNT: " << s_meta->methodCount(); + for (int i=0; i<s_meta->methodCount(); i++) { + QMetaMethod mm = s_meta->method(i); + + QString type; + switch (mm.methodType()) { + case QMetaMethod::Method: + type = "Q_INVOKABLE"; + break; + case QMetaMethod::Signal: + type = "SIGNAL"; + break; + case QMetaMethod::Slot: + type = "SLOT"; + break; + default: + break; + } + + QString returnType = mm.typeName(); + if (returnType == "") returnType = "void"; + + qDebug() << "METHOD" << type << ":" << returnType << mm.signature(); + } + qDebug() << "++++++++++++++++++++++++++++++++++++++++++++++++++++"; + if (!iface) + iface = new QDBusInterface(serviceName, objPath, "", QDBusConnection::sessionBus(), this); + const QMetaObject *i_meta = iface->metaObject(); + qDebug() << "++++++++++++++++++++DBUS SERVICE++++++++++++++++++++"; + qDebug() << i_meta->className(); + qDebug() << "METHOD COUNT: " << i_meta->methodCount(); + for (int i=0; i<i_meta->methodCount(); i++) { + QMetaMethod mm = i_meta->method(i); + + QString type; + switch (mm.methodType()) { + case QMetaMethod::Method: + type = "Q_INVOKABLE"; + break; + case QMetaMethod::Signal: + type = "SIGNAL"; + break; + case QMetaMethod::Slot: + type = "SLOT"; + break; + default: + break; + } + + QString returnType = mm.typeName(); + if (returnType == "") returnType = "void"; + + qDebug() << "METHOD" << type << ":" << returnType << mm.signature(); + } + qDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++"; +#endif + + // Get meta object from type register + const QMetaObject* meta = iManager->metaObject(p.d->entry); + if (!meta) { + qDebug() << "Unknown type" << p.d->entry; + dispatch->writePackage(response); + return; + } + + // Serialize meta object + QByteArray data; + QDataStream stream( &data, QIODevice::WriteOnly | QIODevice::Append ); + QMetaObjectBuilder builder(meta); + builder.serialize(stream); + + // Send meta object and instance ID to the client for processing + d->entry = p.d->entry; + response.d->instanceId = d->serviceInstanceId; + response.d->entry = p.d->entry; + response.d->responseType = QServicePackage::Success; + response.d->payload = QVariant(data); + dispatch->writePackage(response); + } +} + +/*! + Returns the created service instance Id +*/ +QString ObjectEndPoint::getInstanceId() const +{ + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + return d->serviceInstanceId.toString(); +} + +/*! + Client side property call that directly accesses properties through the DBus interface. + Read and reset have special hardcoded DBus methods due to the nature of QtDBus properties + without an adaptor class incorrectly forwarding the metacall type +*/ +QVariant ObjectEndPoint::invokeRemoteProperty(int metaIndex, const QVariant& arg, int /*returnType*/, QMetaObject::Call c ) +{ + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + const QMetaObject *imeta = service->metaObject(); + QMetaProperty property = imeta->property(metaIndex); + + if (c == QMetaObject::WriteProperty) { + // Writing property, direct property DBus call + if (!iface->setProperty(property.name(), arg)) { + qWarning() << "Service property write call failed"; + } + + } else if (c == QMetaObject::ResetProperty) { + // Resetting property, direct special method DBus call + QVariantList args; + args << QVariant(QLatin1String(property.name())); + QDBusMessage msg = iface->callWithArgumentList(QDBus::Block, QLatin1String("propertyReset"), args); + if (msg.type() == QDBusMessage::InvalidMessage) { + qWarning() << "Service property reset call failed"; + } + + } else if (c == QMetaObject::ReadProperty) { + // Reading property, direct special method DBus call + QVariantList args; + args << QVariant(QLatin1String(property.name())); + QDBusMessage msg = iface->callWithArgumentList(QDBus::Block, QLatin1String("propertyRead"), args); + if (msg.type() == QDBusMessage::ReplyMessage) { + QVariantList retList = msg.arguments(); + return retList[0]; + } else { + qWarning() << "Service property read call failed" << msg.errorMessage(); + } + } else { + qWarning() << "Invalid property call"; + } + + return QVariant(); +} + +/*! + Client side method call that converts an argument of type to its corresponding value as a + valid type supported by the QtDBus type system. + + Supports conversion from a QVariant, QList, QMap, QHash, and custom user-defined types. +*/ +QVariant ObjectEndPoint::toDBusVariant(const QByteArray& type, const QVariant& arg) +{ + QVariant dbusVariant = arg; + + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + variantType = QMetaType::type(type); + + if (type == "QVariant") { + // Wrap QVariants in a QDBusVariant + QDBusVariant replacement(arg); + dbusVariant = QVariant::fromValue(replacement); + } else { + // Wrap custom types in a QDBusVariant of the type name and + // a buffer of its variant-wrapped data + QByteArray buffer; + QDataStream stream(&buffer, QIODevice::ReadWrite | QIODevice::Append); + stream << arg; + + QServiceUserTypeDBus customType; + customType.typeName = type; + customType.variantBuffer = buffer; + + QDBusVariant replacement(QVariant::fromValue(customType)); + dbusVariant = QVariant::fromValue(replacement); + } + } + + return dbusVariant; +} + +/*! + Client side method call that directly accesses the object through the DBus interface. + All arguments and return types are processed and converted accordingly so that all functions + satisfy the QtDBus type system. +*/ +QVariant ObjectEndPoint::invokeRemote(int metaIndex, const QVariantList& args, int returnType) +{ + QMetaMethod method = service->metaObject()->method(metaIndex); + + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + // Check is this is a signal relay + if (method.methodType() == QMetaMethod::Signal) { + // Convert custom arguments + QVariantList convertedList; + QList<QByteArray> params = method.parameterTypes(); + for (int i = 0; i < params.size(); i++) { + const QByteArray& type = params[i]; + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + variantType = QMetaType::type(type); + + QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(args[i]); + QVariant variant = dbusVariant.variant(); + + if (type == "QVariant") { + convertedList << variant; + } else { + QByteArray buffer = variant.toByteArray(); + QDataStream stream(&buffer, QIODevice::ReadWrite); + QVariant *customType = new QVariant(variantType, (const void*)0); + QMetaType::load(stream, QMetaType::type("QVariant"), customType); + convertedList << *customType; + } + } else { + convertedList << args[i]; + } + } + + // Signal relay + const int numArgs = convertedList.size(); + QVarLengthArray<void *, 32> a( numArgs+1 ); + a[0] = 0; + + const QList<QByteArray> pTypes = method.parameterTypes(); + for ( int arg = 0; arg < numArgs; ++arg ) { + if (pTypes.at(arg) == "QVariant") { + a[arg+1] = (void *)&( convertedList[arg] ); + } else { + a[arg+1] = (void *)( convertedList[arg].data() ); + } + } + + // Activate the service proxy signal call + QMetaObject::activate(service, metaIndex, a.data()); + return QVariant(); + } + + // Method call so process arguments and convert if not a supported DBus type + QVariantList convertedList; + QList<QByteArray> params = method.parameterTypes(); + for (int i = 0; i < params.size(); i++) { + QVariant converted = toDBusVariant(params[i], args[i]); + convertedList << converted; + } + + bool validDBus = false; + QDBusMessage msg; + + // Find the method name and try a direct DBus call + QString methodName(QLatin1String(method.signature())); + methodName.truncate(methodName.indexOf(QLatin1String("("))); + + if (method.methodType() == QMetaMethod::Slot || method.methodType() == QMetaMethod::Method) { + // Slot or Invokable method + msg = iface->callWithArgumentList(QDBus::Block, methodName, convertedList); + if (msg.type() == QDBusMessage::ReplyMessage) { + validDBus = true; + } + } + + // DBus call should only fail for methods with invalid type definitions + if (validDBus) { + if (returnType == QMetaType::Void) { + // Void method call + return QVariant(); + } + else { + // Use DBus message return value + QVariantList retList = msg.arguments(); + + // Process return + const QByteArray& retType = QByteArray(method.typeName()); + int variantType = QVariant::nameToType(retType); + if (variantType == QVariant::UserType) { + variantType = QMetaType::type(retType); + + if (retType == "QVariant") { + // QVariant return from QDBusVariant wrapper + QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(retList[0]); + return dbusVariant.variant(); + } else { + // Custom return type + QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(retList[0]); + QVariant convert = dbusVariant.variant(); + + QServiceUserTypeDBus customType = qdbus_cast<QServiceUserTypeDBus>(convert); + QByteArray buffer = customType.variantBuffer; + QDataStream stream(&buffer, QIODevice::ReadWrite); + + // Load our buffered variant-wrapped custom return + QVariant *customReturn = new QVariant(variantType, (const void*)0); + QMetaType::load(stream, QMetaType::type("QVariant"), customReturn); + + return QVariant(variantType, customReturn->data()); + } + } else { + // Standard return type + return retList[0]; + } + } + } else { + qWarning( "%s::%s cannot be called.", service->metaObject()->className(), method.signature()); + } + + return QVariant(); +} + +/*! + Client side waits for service side requested +*/ +void ObjectEndPoint::waitForResponse(const QUuid& requestId) +{ + Q_ASSERT(d->endPointType == ObjectEndPoint::Client); + + if (openRequests()->contains(requestId) ) { + Response* response = openRequests()->value(requestId); + QEventLoop* loop = new QEventLoop( this ); + connect(this, SIGNAL(pendingRequestFinished()), loop, SLOT(quit())); + + while(!response->isFinished) { + loop->exec(); + } + + delete loop; + } +} + +#include "moc_objectendpoint_dbus_p.cpp" + +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/objectendpoint_dbus_p.h b/src/serviceframework/ipc/objectendpoint_dbus_p.h new file mode 100644 index 00000000..a8d3133f --- /dev/null +++ b/src/serviceframework/ipc/objectendpoint_dbus_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECT_ENDPOINT_DBUS_H +#define OBJECT_ENDPOINT_DBUS_H + +#include "qserviceframeworkglobal.h" +#include "ipcendpoint_p.h" +#include "qremoteserviceregister.h" +#include "qservice.h" +#include <QPointer> +#include <QHash> +#include <QtDBus> +#include "qservicemetaobject_dbus_p.h" + +QTM_BEGIN_NAMESPACE + +class QServiceMetaObjectDBus; +class ObjectEndPointPrivate; +class ObjectEndPoint : public QObject +{ + Q_OBJECT +public: + enum Type { + Service = 0, + Client + }; + + ObjectEndPoint(Type type, QServiceIpcEndPoint* comm, QObject* parent = 0); + ~ObjectEndPoint(); + + QObject* constructProxy(const QRemoteServiceRegister::Entry& entry); + + void objectRequest(const QServicePackage& p); + void methodCall(const QServicePackage& p); + void propertyCall(const QServicePackage& p); + + QString getInstanceId() const; + + QVariant invokeRemote(int metaIndex, const QVariantList& args, int returnType); + QVariant invokeRemoteProperty(int metaIndex, const QVariant& arg, int returnType, QMetaObject::Call c); + +Q_SIGNALS: + void pendingRequestFinished(); + +public Q_SLOTS: + void newPackageReady(); + void disconnected(const QString& clientId, const QString & instanceId); + void unregisterObjectDBus(const QRemoteServiceRegister::Entry& entry, const QUuid& id); + +private: + void waitForResponse(const QUuid& requestId); + QVariant toDBusVariant(const QByteArray& type, const QVariant& arg); + + QServiceIpcEndPoint* dispatch; + QPointer<QObject> service; + ObjectEndPointPrivate* d; + + QDBusInterface *iface; + QServiceMetaObjectDBus *signalsObject; +}; + +QTM_END_NAMESPACE + +#endif //OBJECT_ENDPOINT_DBUS_H diff --git a/src/serviceframework/ipc/objectendpoint_p.h b/src/serviceframework/ipc/objectendpoint_p.h new file mode 100644 index 00000000..7cd67e20 --- /dev/null +++ b/src/serviceframework/ipc/objectendpoint_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECT_ENDPOINT_H +#define OBJECT_ENDPOINT_H + +#include "qserviceframeworkglobal.h" +#include "ipcendpoint_p.h" +#include "qremoteserviceregister.h" +#include "qservice.h" +#include <QPointer> +#include <QHash> + +QTM_BEGIN_NAMESPACE + +class ObjectEndPointPrivate; +class ObjectEndPoint : public QObject +{ + Q_OBJECT +public: + enum Type { + Service = 0, + Client + }; + + ObjectEndPoint(Type type, QServiceIpcEndPoint* comm, QObject* parent = 0); + ~ObjectEndPoint(); + QObject* constructProxy(const QRemoteServiceRegister::Entry& entry); + + void objectRequest(const QServicePackage& p); + void methodCall(const QServicePackage& p); + void propertyCall(const QServicePackage& p); + + QVariant invokeRemote(int metaIndex, const QVariantList& args, int returnType); + QVariant invokeRemoteProperty(int metaIndex, const QVariant& arg, int returnType, QMetaObject::Call c); + +Q_SIGNALS: + void pendingRequestFinished(); + +public Q_SLOTS: + void newPackageReady(); + void disconnected(); + +private: + void waitForResponse(const QUuid& requestId); + + QServiceIpcEndPoint* dispatch; + QPointer<QObject> service; + ObjectEndPointPrivate* d; +}; + +QTM_END_NAMESPACE + +#endif //OBJECT_ENDPOINT_H diff --git a/src/serviceframework/ipc/proxyobject.cpp b/src/serviceframework/ipc/proxyobject.cpp new file mode 100644 index 00000000..4ba5179f --- /dev/null +++ b/src/serviceframework/ipc/proxyobject.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "proxyobject_p.h" +#include "qmetaobjectbuilder_p.h" +#include "qremoteserviceregisterentry_p.h" + +#include <QDebug> + +QTM_BEGIN_NAMESPACE + +class QServiceProxyPrivate +{ +public: + QByteArray metadata; + QMetaObject* meta; + ObjectEndPoint* endPoint; +}; + +QServiceProxy::QServiceProxy(const QByteArray& metadata, ObjectEndPoint* endPoint, QObject* parent) + : QObject(parent) +{ + Q_ASSERT(endPoint); + d = new QServiceProxyPrivate(); + d->metadata = metadata; + d->meta = 0; + d->endPoint = endPoint; + + QDataStream stream(d->metadata); + QMetaObjectBuilder builder; + QMap<QByteArray, const QMetaObject*> refs; + + builder.deserialize(stream, refs); + if (stream.status() != QDataStream::Ok) { + qWarning() << "Invalid metaObject for service received"; + } else { + QMetaMethodBuilder b = builder.addSignal("errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)"); + + // After all methods are filled in, otherwise qvector won't be big enough + localSignals.fill(false, builder.methodCount()); + localSignals.replace(b.index(), true); // Call activate locally + + d->meta = builder.toMetaObject(); + } +} + +QServiceProxy::~QServiceProxy() +{ + if (d->meta) + qFree(d->meta); + delete d; +} + +//provide custom Q_OBJECT implementation +const QMetaObject* QServiceProxy::metaObject() const +{ + return d->meta; +} + +int QServiceProxy::qt_metacall(QMetaObject::Call c, int id, void **a) +{ + id = QObject::qt_metacall(c, id, a); + if (id < 0 || !d->meta) + return id; + + if (localSignals.at(id)){ + QMetaObject::activate(this, d->meta, id, a); + return id; + } + + if (c == QMetaObject::InvokeMetaMethod) { + const int mcount = d->meta->methodCount() - d->meta->methodOffset(); + const int metaIndex = id + d->meta->methodOffset(); + + QMetaMethod method = d->meta->method(metaIndex); + + const int returnType = QMetaType::type(method.typeName()); + + //process arguments + const QList<QByteArray> pTypes = method.parameterTypes(); + const int pTypesCount = pTypes.count(); + QVariantList args ; + if (pTypesCount > 10) { + qWarning() << "Cannot call" << method.signature() << ". More than 10 parameter."; + return id; + } + for (int i=0; i < pTypesCount; i++) { + const QByteArray& t = pTypes[i]; + + int variantType = QVariant::nameToType(t); + if (variantType == QVariant::UserType) + variantType = QMetaType::type(t); + + if (t == "QVariant") { //ignore whether QVariant is declared as metatype + args << *reinterpret_cast<const QVariant(*)>(a[i+1]); + } else if ( variantType == 0 ){ + qWarning("%s: argument %s has unknown type. Use qRegisterMetaType to register it.", + method.signature(), t.data()); + return id; + } else { + args << QVariant(variantType, a[i+1]); + } + } + + //QVariant looks the same as Void type. we need to distinguish them + if (returnType == QMetaType::Void && strcmp(method.typeName(),"QVariant") ) { + d->endPoint->invokeRemote(metaIndex, args, returnType); + } else { + //TODO: ugly but works + //add +1 if we have a variant return type to avoid triggering of void + //code path + //invokeRemote() parameter list needs review + QVariant result = d->endPoint->invokeRemote(metaIndex, args, + returnType==0 ? returnType+1: returnType); + if (result.type() != QVariant::Invalid){ + if (returnType != 0 && strcmp(method.typeName(),"QVariant")) { + QByteArray buffer; + QDataStream stream(&buffer, QIODevice::ReadWrite); + QMetaType::save(stream, returnType, result.constData()); + stream.device()->seek(0); + QMetaType::load(stream, returnType, a[0]); + } else { + if (a[0]) *reinterpret_cast< QVariant*>(a[0]) = result; + } + } + } + id-=mcount; + } else if ( c == QMetaObject::ReadProperty + || c == QMetaObject::WriteProperty + || c == QMetaObject::ResetProperty ) { + const int pCount = d->meta->propertyCount() - d->meta->propertyOffset(); + const int metaIndex = id + d->meta->propertyOffset(); + QMetaProperty property = d->meta->property(metaIndex); + if (property.isValid()) { + int pType = property.type(); + if (pType == QVariant::UserType) + pType = QMetaType::type(property.typeName()); + + QVariant arg; + if ( c == QMetaObject::WriteProperty ) { + if (pType == QVariant::Invalid && QByteArray(property.typeName()) == "QVariant") + arg = *reinterpret_cast<const QVariant(*)>(a[0]); + else if (pType == 0) { + qWarning("%s: property %s has unkown type", property.name(), property.typeName()); + return id; + } else { + arg = QVariant(pType, a[0]); + } + } + QVariant result; + if (c == QMetaObject::ReadProperty) { + result = d->endPoint->invokeRemoteProperty(metaIndex, arg, pType, c); + //wrap result for client + if (pType != 0) { + QByteArray buffer; + QDataStream stream(&buffer, QIODevice::ReadWrite); + QMetaType::save(stream, pType, result.constData()); + stream.device()->seek(0); + QMetaType::load(stream, pType, a[0]); + } else { + if (a[0]) *reinterpret_cast< QVariant*>(a[0]) = result; + } + } else { + d->endPoint->invokeRemoteProperty(metaIndex, arg, pType, c); + } + } + id-=pCount; + } else if ( c == QMetaObject::QueryPropertyDesignable + || c == QMetaObject::QueryPropertyScriptable + || c == QMetaObject::QueryPropertyStored + || c == QMetaObject::QueryPropertyEditable + || c == QMetaObject::QueryPropertyUser ) + { + //Nothing to do? + //These values are part of the transferred meta object already + } else { + //TODO + qWarning() << "MetaCall type" << c << "not yet handled"; + } + return id; +} + +void *QServiceProxy::qt_metacast(const char* className) +{ + if (!className) return 0; + //this object should not be castable to anything but QObject + return QObject::qt_metacast(className); +} +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/proxyobject_p.h b/src/serviceframework/ipc/proxyobject_p.h new file mode 100644 index 00000000..098a3b3a --- /dev/null +++ b/src/serviceframework/ipc/proxyobject_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROXY_OBJECT_H +#define PROXY_OBJECT_H + +#include "qserviceframeworkglobal.h" + +#ifdef QT_NO_DBUS + #include "objectendpoint_p.h" +#else + #include "objectendpoint_dbus_p.h" +#endif + +#include <QObject> + +QTM_BEGIN_NAMESPACE + +class QServiceProxyPrivate; +class QServiceProxy : public QObject +{ + //TODO make inherit from QRemoteService + //Note: Do not put Q_OBJECT here +public: + QServiceProxy(const QByteArray& metadata, ObjectEndPoint* endpoint, QObject* parent = 0); + virtual ~QServiceProxy(); + + //provide custom Q_OBJECT implementation + virtual const QMetaObject* metaObject() const; + int qt_metacall(QMetaObject::Call c, int id, void **a); + void *qt_metacast(const char* className); + +private: + QServiceProxyPrivate* d; + QVector<bool> localSignals; +}; + + + +QTM_END_NAMESPACE + +#endif //PROXY_OBJECT_H diff --git a/src/serviceframework/ipc/qmetaobjectbuilder.cpp b/src/serviceframework/ipc/qmetaobjectbuilder.cpp new file mode 100644 index 00000000..acb98b81 --- /dev/null +++ b/src/serviceframework/ipc/qmetaobjectbuilder.cpp @@ -0,0 +1,2611 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmetaobjectbuilder_p.h" +#include <QDebug> + +#ifndef Q_OS_WIN +#include <stdint.h> +#endif + +QTM_BEGIN_NAMESPACE + +/*! + \class QMetaObjectBuilder + \internal + \brief The QMetaObjectBuilder class supports building QMetaObject objects at runtime. + \since 1.1 + +*/ + +/*! + \enum QMetaObjectBuilder::AddMember + This enum defines which members of QMetaObject should be copied by QMetaObjectBuilder::addMetaObject() + + \value ClassName Add the class name. + \value SuperClass Add the super class. + \value Methods Add methods that aren't signals or slots. + \value Signals Add signals. + \value Slots Add slots. + \value Constructors Add constructors. + \value Properties Add properties. + \value Enumerators Add enumerators. + \value ClassInfos Add items of class information. + \value RelatedMetaObjects Add related meta objects. + \value StaticMetacall Add the static metacall function. + \value PublicMethods Add public methods (ignored for signals). + \value ProtectedMethods Add protected methods (ignored for signals). + \value PrivateMethods All private methods (ignored for signals). + \value AllMembers Add all members. + \value AllPrimaryMembers Add everything except the class name, super class, and static metacall function. +*/ + +// copied from moc's generator.cpp +uint qvariant_nameToType(const char* name) +{ + if (!name) + return 0; + + if (strcmp(name, "QVariant") == 0) + return 0xffffffff; + if (strcmp(name, "QCString") == 0) + return QMetaType::QByteArray; + if (strcmp(name, "Q_LLONG") == 0) + return QMetaType::LongLong; + if (strcmp(name, "Q_ULLONG") == 0) + return QMetaType::ULongLong; + if (strcmp(name, "QIconSet") == 0) + return QMetaType::QIcon; + + uint tp = QMetaType::type(name); + return tp < QMetaType::User ? tp : 0; +} + +/* + Returns true if the type is a QVariant types. +*/ +bool isVariantType(const char* type) +{ + return qvariant_nameToType(type) != 0; +} + +// copied from qmetaobject.cpp +// do not touch without touching the moc as well +enum PropertyFlags { + Invalid = 0x00000000, + Readable = 0x00000001, + Writable = 0x00000002, + Resettable = 0x00000004, + EnumOrFlag = 0x00000008, + StdCppSet = 0x00000100, +// Override = 0x00000200, + Constant = 0x00000400, + Final = 0x00000800, + Designable = 0x00001000, + ResolveDesignable = 0x00002000, + Scriptable = 0x00004000, + ResolveScriptable = 0x00008000, + Stored = 0x00010000, + ResolveStored = 0x00020000, + Editable = 0x00040000, + ResolveEditable = 0x00080000, + User = 0x00100000, + ResolveUser = 0x00200000, + Notify = 0x00400000, + Revisioned = 0x00800000 +}; + +enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + AccessMask = 0x03, //mask + + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodConstructor = 0x0c, + MethodTypeMask = 0x0c, + + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40, + MethodRevisioned = 0x80 +}; + +struct QMetaObjectPrivate +{ + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + int enumeratorCount, enumeratorData; + int constructorCount, constructorData; + int flags; +}; + +static inline const QMetaObjectPrivate *priv(const uint* data) +{ return reinterpret_cast<const QMetaObjectPrivate*>(data); } +// end of copied lines from qmetaobject.cpp + +class QMetaMethodBuilderPrivate +{ +public: + QMetaMethodBuilderPrivate + (QMetaMethod::MethodType _methodType, + const QByteArray& _signature, + const QByteArray& _returnType = QByteArray(), + QMetaMethod::Access _access = QMetaMethod::Public) + : signature(QMetaObject::normalizedSignature(_signature.constData())), + returnType(QMetaObject::normalizedType(_returnType)), + attributes(((int)_access) | (((int)_methodType) << 2)) + { + } + + QByteArray signature; + QByteArray returnType; + QList<QByteArray> parameterNames; + QByteArray tag; + int attributes; + + QMetaMethod::MethodType methodType() const + { + return (QMetaMethod::MethodType)((attributes & MethodTypeMask) >> 2); + } + + QMetaMethod::Access access() const + { + return (QMetaMethod::Access)(attributes & AccessMask); + } + + void setAccess(QMetaMethod::Access value) + { + attributes = ((attributes & ~AccessMask) | (int)value); + } +}; + +class QMetaPropertyBuilderPrivate +{ +public: + QMetaPropertyBuilderPrivate + (const QByteArray& _name, const QByteArray& _type, int notifierIdx=-1) + : name(_name), + type(QMetaObject::normalizedType(_type.constData())), + flags(Readable | Writable | Scriptable), notifySignal(-1) + { + if (notifierIdx >= 0) { + flags |= Notify; + notifySignal = notifierIdx; + } + } + + QByteArray name; + QByteArray type; + int flags; + int notifySignal; + + bool flag(int f) const + { + return ((flags & f) != 0); + } + + void setFlag(int f, bool value) + { + if (value) + flags |= f; + else + flags &= ~f; + } +}; + +class QMetaEnumBuilderPrivate +{ +public: + QMetaEnumBuilderPrivate(const QByteArray& _name) + : name(_name), isFlag(false) + { + } + + QByteArray name; + bool isFlag; + QList<QByteArray> keys; + QList<int> values; +}; + +class QMetaObjectBuilderPrivate +{ +public: + QMetaObjectBuilderPrivate() + : flags(0) + { + superClass = &QObject::staticMetaObject; + staticMetacallFunction = 0; + } + + QByteArray className; + const QMetaObject *superClass; + QMetaObjectBuilder::StaticMetacallFunction staticMetacallFunction; + QList<QMetaMethodBuilderPrivate> methods; + QList<QMetaMethodBuilderPrivate> constructors; + QList<QMetaPropertyBuilderPrivate> properties; + QList<QByteArray> classInfoNames; + QList<QByteArray> classInfoValues; + QList<QMetaEnumBuilderPrivate> enumerators; +#ifdef Q_NO_DATA_RELOCATION + QList<QMetaObjectAccessor> relatedMetaObjects; +#else + QList<const QMetaObject *> relatedMetaObjects; +#endif + int flags; +}; + +/*! + Constructs a new QMetaObjectBuilder. +*/ +QMetaObjectBuilder::QMetaObjectBuilder() +{ + d = new QMetaObjectBuilderPrivate(); +} + +/*! + Constructs a new QMetaObjectBuilder which is a copy of the + meta object information in \a prototype. Note: the super class + contents for \a prototype are not copied, only the immediate + class that is defined by \a prototype. + + The \a members parameter indicates which members of \a prototype + should be added. The default is AllMembers. + + \sa addMetaObject() +*/ +QMetaObjectBuilder::QMetaObjectBuilder + (const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members) +{ + d = new QMetaObjectBuilderPrivate(); + addMetaObject(prototype, members); +} + +/*! + Destroys this meta object builder. +*/ +QMetaObjectBuilder::~QMetaObjectBuilder() +{ + delete d; +} + +/*! + Returns the name of the class being constructed by this + meta object builder. The default value is an empty QByteArray. + + \sa setClassName(), superClass() +*/ +QByteArray QMetaObjectBuilder::className() const +{ + return d->className; +} + +/*! + Sets the \a name of the class being constructed by this + meta object builder. + + \sa className(), setSuperClass() +*/ +void QMetaObjectBuilder::setClassName(const QByteArray& name) +{ + d->className = name; +} + +/*! + Returns the superclass meta object of the class being constructed + by this meta object builder. The default value is the meta object + for QObject. + + \sa setSuperClass(), className() +*/ +const QMetaObject *QMetaObjectBuilder::superClass() const +{ + return d->superClass; +} + +/*! + Sets the superclass meta object of the class being constructed + by this meta object builder to \a meta. The \a meta parameter + must not be null. + + \sa superClass(), setClassName() +*/ +void QMetaObjectBuilder::setSuperClass(const QMetaObject *meta) +{ + Q_ASSERT(meta); + d->superClass = meta; +} + +/*! + Returns the flags of the class being constructed by this meta object + builder. + + \sa setFlags() +*/ +QMetaObjectBuilder::MetaObjectFlags QMetaObjectBuilder::flags() const +{ + return (QMetaObjectBuilder::MetaObjectFlags)d->flags; +} + +/*! + Sets the \a flags of the class being constructed by this meta object + builder. + + \sa flags() +*/ +void QMetaObjectBuilder::setFlags(MetaObjectFlags flags) +{ + d->flags = flags; +} + +/*! + Returns the number of methods in this class, excluding the number + of methods in the base class. These include signals and slots + as well as normal member functions. + + \sa addMethod(), method(), removeMethod(), indexOfMethod() +*/ +int QMetaObjectBuilder::methodCount() const +{ + return d->methods.size(); +} + +/*! + Returns the number of constructors in this class. + + \sa addConstructor(), constructor(), removeConstructor(), indexOfConstructor() +*/ +int QMetaObjectBuilder::constructorCount() const +{ + return d->constructors.size(); +} + +/*! + Returns the number of properties in this class, excluding the number + of properties in the base class. + + \sa addProperty(), property(), removeProperty(), indexOfProperty() +*/ +int QMetaObjectBuilder::propertyCount() const +{ + return d->properties.size(); +} + +/*! + Returns the number of enumerators in this class, excluding the + number of enumerators in the base class. + + \sa addEnumerator(), enumerator(), removeEnumerator() + \sa indexOfEnumerator() +*/ +int QMetaObjectBuilder::enumeratorCount() const +{ + return d->enumerators.size(); +} + +/*! + Returns the number of items of class information in this class, + exclusing the number of items of class information in the base class. + + \sa addClassInfo(), classInfoName(), classInfoValue(), removeClassInfo() + \sa indexOfClassInfo() +*/ +int QMetaObjectBuilder::classInfoCount() const +{ + return d->classInfoNames.size(); +} + +/*! + Returns the number of related meta objects that are associated + with this class. + + Related meta objects are used when resolving the enumerated type + associated with a property, where the enumerated type is in a + different class from the property. + + \sa addRelatedMetaObject(), relatedMetaObject() + \sa removeRelatedMetaObject() +*/ +int QMetaObjectBuilder::relatedMetaObjectCount() const +{ + return d->relatedMetaObjects.size(); +} + +/*! + Adds a new public method to this class with the specified \a signature. + Returns an object that can be used to adjust the other attributes + of the method. The \a signature will be normalized before it is + added to the class. + + \sa method(), methodCount(), removeMethod(), indexOfMethod() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QByteArray& signature) +{ + int index = d->methods.size(); + d->methods.append(QMetaMethodBuilderPrivate(QMetaMethod::Method, signature)); + return QMetaMethodBuilder(this, index); +} + +/*! + Adds a new public method to this class with the specified + \a signature and \a returnType. Returns an object that can be + used to adjust the other attributes of the method. The \a signature + and \a returnType will be normalized before they are added to + the class. If \a returnType is empty, then it indicates that + the method has \c{void} as its return type. + + \sa method(), methodCount(), removeMethod(), indexOfMethod() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addMethod + (const QByteArray& signature, const QByteArray& returnType) +{ + int index = d->methods.size(); + d->methods.append(QMetaMethodBuilderPrivate + (QMetaMethod::Method, signature, returnType)); + return QMetaMethodBuilder(this, index); +} + +/*! + Adds a new public method to this class that has the same information as + \a prototype. This is used to clone the methods of an existing + QMetaObject. Returns an object that can be used to adjust the + attributes of the method. + + This function will detect if \a prototype is an ordinary method, + signal, slot, or constructor and act accordingly. + + \sa method(), methodCount(), removeMethod(), indexOfMethod() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QMetaMethod& prototype) +{ + QMetaMethodBuilder method; + if (prototype.methodType() == QMetaMethod::Method) + method = addMethod(prototype.signature()); + else if (prototype.methodType() == QMetaMethod::Signal) + method = addSignal(prototype.signature()); + else if (prototype.methodType() == QMetaMethod::Slot) + method = addSlot(prototype.signature()); + else if (prototype.methodType() == QMetaMethod::Constructor) + method = addConstructor(prototype.signature()); + method.setReturnType(prototype.typeName()); + method.setParameterNames(prototype.parameterNames()); + method.setTag(prototype.tag()); + method.setAccess(prototype.access()); + method.setAttributes(prototype.attributes()); + return method; +} + +/*! + Adds a new public slot to this class with the specified \a signature. + Returns an object that can be used to adjust the other attributes + of the slot. The \a signature will be normalized before it is + added to the class. + + \sa addMethod(), addSignal(), indexOfSlot() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addSlot(const QByteArray& signature) +{ + int index = d->methods.size(); + d->methods.append(QMetaMethodBuilderPrivate(QMetaMethod::Slot, signature)); + return QMetaMethodBuilder(this, index); +} + +/*! + Adds a new signal to this class with the specified \a signature. + Returns an object that can be used to adjust the other attributes + of the signal. The \a signature will be normalized before it is + added to the class. + + \sa addMethod(), addSlot(), indexOfSignal() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addSignal(const QByteArray& signature) +{ + int index = d->methods.size(); + d->methods.append(QMetaMethodBuilderPrivate + (QMetaMethod::Signal, signature, QByteArray(), QMetaMethod::Protected)); + return QMetaMethodBuilder(this, index); +} + +/*! + Adds a new constructor to this class with the specified \a signature. + Returns an object that can be used to adjust the other attributes + of the constructor. The \a signature will be normalized before it is + added to the class. + + \sa constructor(), constructorCount(), removeConstructor() + \sa indexOfConstructor() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QByteArray& signature) +{ + int index = d->constructors.size(); + d->constructors.append(QMetaMethodBuilderPrivate(QMetaMethod::Constructor, signature)); + return QMetaMethodBuilder(this, -(index + 1)); +} + +/*! + Adds a new constructor to this class that has the same information as + \a prototype. This is used to clone the constructors of an existing + QMetaObject. Returns an object that can be used to adjust the + attributes of the constructor. + + This function requires that \a prototype be a constructor. + + \sa constructor(), constructorCount(), removeConstructor() + \sa indexOfConstructor() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QMetaMethod& prototype) +{ + Q_ASSERT(prototype.methodType() == QMetaMethod::Constructor); + QMetaMethodBuilder ctor = addConstructor(prototype.signature()); + ctor.setReturnType(prototype.typeName()); + ctor.setParameterNames(prototype.parameterNames()); + ctor.setTag(prototype.tag()); + ctor.setAccess(prototype.access()); + ctor.setAttributes(prototype.attributes()); + return ctor; +} + +/*! + Adds a new readable/writable property to this class with the + specified \a name and \a type. Returns an object that can be used + to adjust the other attributes of the property. The \a type will + be normalized before it is added to the class. \a notifierId will + be registered as the property's \i notify signal. + + \sa property(), propertyCount(), removeProperty(), indexOfProperty() +*/ +QMetaPropertyBuilder QMetaObjectBuilder::addProperty + (const QByteArray& name, const QByteArray& type, int notifierId) +{ + int index = d->properties.size(); + d->properties.append(QMetaPropertyBuilderPrivate(name, type, notifierId)); + return QMetaPropertyBuilder(this, index); +} + +/*! + Adds a new property to this class that has the same information as + \a prototype. This is used to clone the properties of an existing + QMetaObject. Returns an object that can be used to adjust the + attributes of the property. + + \sa property(), propertyCount(), removeProperty(), indexOfProperty() +*/ +QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty& prototype) +{ + QMetaPropertyBuilder property = addProperty(prototype.name(), prototype.typeName()); + property.setReadable(prototype.isReadable()); + property.setWritable(prototype.isWritable()); + property.setResettable(prototype.isResettable()); + property.setDesignable(prototype.isDesignable()); + property.setScriptable(prototype.isScriptable()); + property.setStored(prototype.isStored()); + property.setEditable(prototype.isEditable()); + property.setUser(prototype.isUser()); + property.setStdCppSet(prototype.hasStdCppSet()); + property.setEnumOrFlag(prototype.isEnumType()); + property.setConstant(prototype.isConstant()); + property.setFinal(prototype.isFinal()); + if (prototype.hasNotifySignal()) { + // Find an existing method for the notify signal, or add a new one. + QMetaMethod method = prototype.notifySignal(); + int index = indexOfMethod(method.signature()); + if (index == -1) + index = addMethod(method).index(); + d->properties[property._index].notifySignal = index; + d->properties[property._index].setFlag(Notify, true); + } + return property; +} + +/*! + Adds a new enumerator to this class with the specified + \a name. Returns an object that can be used to adjust + the other attributes of the enumerator. + + \sa enumerator(), enumeratorCount(), removeEnumerator(), + \sa indexOfEnumerator() +*/ +QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QByteArray& name) +{ + int index = d->enumerators.size(); + d->enumerators.append(QMetaEnumBuilderPrivate(name)); + return QMetaEnumBuilder(this, index); +} + +/*! + Adds a new enumerator to this class that has the same information as + \a prototype. This is used to clone the enumerators of an existing + QMetaObject. Returns an object that can be used to adjust the + attributes of the enumerator. + + \sa enumerator(), enumeratorCount(), removeEnumerator(), + \sa indexOfEnumerator() +*/ +QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum& prototype) +{ + QMetaEnumBuilder en = addEnumerator(prototype.name()); + en.setIsFlag(prototype.isFlag()); + int count = prototype.keyCount(); + for (int index = 0; index < count; ++index) + en.addKey(prototype.key(index), prototype.value(index)); + return en; +} + +/*! + Adds \a name and \a value as an item of class information to this class. + Returns the index of the new item of class information. + + \sa classInfoCount(), classInfoName(), classInfoValue(), removeClassInfo() + \sa indexOfClassInfo() +*/ +int QMetaObjectBuilder::addClassInfo(const QByteArray& name, const QByteArray& value) +{ + int index = d->classInfoNames.size(); + d->classInfoNames += name; + d->classInfoValues += value; + return index; +} + +/*! + Adds \a meta to this class as a related meta object. Returns + the index of the new related meta object entry. + + Related meta objects are used when resolving the enumerated type + associated with a property, where the enumerated type is in a + different class from the property. + + \sa relatedMetaObjectCount(), relatedMetaObject() + \sa removeRelatedMetaObject() +*/ +#ifdef Q_NO_DATA_RELOCATION +int QMetaObjectBuilder::addRelatedMetaObject(const QMetaObjectAccessor &meta) +#else +int QMetaObjectBuilder::addRelatedMetaObject(const QMetaObject *meta) +#endif +{ + Q_ASSERT(meta); + int index = d->relatedMetaObjects.size(); + d->relatedMetaObjects.append(meta); + return index; +} + +/*! + Adds the contents of \a prototype to this meta object builder. + This function is useful for cloning the contents of an existing QMetaObject. + + The \a members parameter indicates which members of \a prototype + should be added. The default is AllMembers. +*/ +void QMetaObjectBuilder::addMetaObject + (const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members) +{ + Q_ASSERT(prototype); + int index; + + if ((members & ClassName) != 0) + d->className = prototype->className(); + + if ((members & SuperClass) != 0) + d->superClass = prototype->superClass(); + + if ((members & (Methods | Signals | Slots)) != 0) { + for (index = prototype->methodOffset(); index < prototype->methodCount(); ++index) { + QMetaMethod method = prototype->method(index); + if (method.methodType() != QMetaMethod::Signal) { + if (method.access() == QMetaMethod::Public && (members & PublicMethods) == 0) + continue; + if (method.access() == QMetaMethod::Private && (members & PrivateMethods) == 0) + continue; + if (method.access() == QMetaMethod::Protected && (members & ProtectedMethods) == 0) + continue; + } + if (method.methodType() == QMetaMethod::Method && (members & Methods) != 0) { + addMethod(method); + } else if (method.methodType() == QMetaMethod::Signal && + (members & Signals) != 0) { + addMethod(method); + } else if (method.methodType() == QMetaMethod::Slot && + (members & Slots) != 0) { + addMethod(method); + } + } + } + + if ((members & Constructors) != 0) { + for (index = 0; index < prototype->constructorCount(); ++index) + addConstructor(prototype->constructor(index)); + } + + if ((members & Properties) != 0) { + for (index = prototype->propertyOffset(); index < prototype->propertyCount(); ++index) + addProperty(prototype->property(index)); + } + + if ((members & Enumerators) != 0) { + for (index = prototype->enumeratorOffset(); index < prototype->enumeratorCount(); ++index) + addEnumerator(prototype->enumerator(index)); + } + + if ((members & ClassInfos) != 0) { + for (index = prototype->classInfoOffset(); index < prototype->classInfoCount(); ++index) { + QMetaClassInfo ci = prototype->classInfo(index); + addClassInfo(ci.name(), ci.value()); + } + } + + if ((members & RelatedMetaObjects) != 0) { +#ifdef Q_NO_DATA_RELOCATION + const QMetaObjectAccessor *objects = 0; +#else + const QMetaObject **objects; + if (priv(prototype->d.data)->revision < 2) { + objects = (const QMetaObject **)(prototype->d.extradata); + } else +#endif + { + const QMetaObjectExtraData *extra = (const QMetaObjectExtraData *)(prototype->d.extradata); + if (extra) + objects = extra->objects; + else + objects = 0; + } + if (objects) { + while (*objects != 0) { + addRelatedMetaObject(*objects); + ++objects; + } + } + } + + if ((members & StaticMetacall) != 0) { + if (priv(prototype->d.data)->revision >= 6) { + const QMetaObjectExtraData *extra = + (const QMetaObjectExtraData *)(prototype->d.extradata); + if (extra && extra->static_metacall) + setStaticMetacallFunction(extra->static_metacall); + } + } +} + +/*! + Returns the method at \a index in this class. + + \sa methodCount(), addMethod(), removeMethod(), indexOfMethod() +*/ +QMetaMethodBuilder QMetaObjectBuilder::method(int index) const +{ + if (index >= 0 && index < d->methods.size()) + return QMetaMethodBuilder(this, index); + else + return QMetaMethodBuilder(); +} + +/*! + Returns the constructor at \a index in this class. + + \sa methodCount(), addMethod(), removeMethod(), indexOfConstructor() +*/ +QMetaMethodBuilder QMetaObjectBuilder::constructor(int index) const +{ + if (index >= 0 && index < d->constructors.size()) + return QMetaMethodBuilder(this, -(index + 1)); + else + return QMetaMethodBuilder(); +} + +/*! + Returns the property at \a index in this class. + + \sa methodCount(), addMethod(), removeMethod(), indexOfProperty() +*/ +QMetaPropertyBuilder QMetaObjectBuilder::property(int index) const +{ + if (index >= 0 && index < d->properties.size()) + return QMetaPropertyBuilder(this, index); + else + return QMetaPropertyBuilder(); +} + +/*! + Returns the enumerator at \a index in this class. + + \sa enumeratorCount(), addEnumerator(), removeEnumerator() + \sa indexOfEnumerator() +*/ +QMetaEnumBuilder QMetaObjectBuilder::enumerator(int index) const +{ + if (index >= 0 && index < d->enumerators.size()) + return QMetaEnumBuilder(this, index); + else + return QMetaEnumBuilder(); +} + +/*! + Returns the related meta object at \a index in this class. + + Related meta objects are used when resolving the enumerated type + associated with a property, where the enumerated type is in a + different class from the property. + + \sa relatedMetaObjectCount(), addRelatedMetaObject() + \sa removeRelatedMetaObject() +*/ +const QMetaObject *QMetaObjectBuilder::relatedMetaObject(int index) const +{ + if (index >= 0 && index < d->relatedMetaObjects.size()) +#ifdef Q_NO_DATA_RELOCATION + return &((*(d->relatedMetaObjects[index]))()); +#else + return d->relatedMetaObjects[index]; +#endif + else + return 0; +} + +/*! + Returns the name of the item of class information at \a index + in this class. + + \sa classInfoCount(), addClassInfo(), classInfoValue(), removeClassInfo() + \sa indexOfClassInfo() +*/ +QByteArray QMetaObjectBuilder::classInfoName(int index) const +{ + if (index >= 0 && index < d->classInfoNames.size()) + return d->classInfoNames[index]; + else + return QByteArray(); +} + +/*! + Returns the value of the item of class information at \a index + in this class. + + \sa classInfoCount(), addClassInfo(), classInfoName(), removeClassInfo() + \sa indexOfClassInfo() +*/ +QByteArray QMetaObjectBuilder::classInfoValue(int index) const +{ + if (index >= 0 && index < d->classInfoValues.size()) + return d->classInfoValues[index]; + else + return QByteArray(); +} + +/*! + Removes the method at \a index from this class. The indices of + all following methods will be adjusted downwards by 1. If the + method is registered as a notify signal on a property, then the + notify signal will be removed from the property. + + \sa methodCount(), addMethod(), method(), indexOfMethod() +*/ +void QMetaObjectBuilder::removeMethod(int index) +{ + if (index >= 0 && index < d->methods.size()) { + d->methods.removeAt(index); + for (int prop = 0; prop < d->properties.size(); ++prop) { + // Adjust the indices of property notify signal references. + if (d->properties[prop].notifySignal == index) { + d->properties[prop].notifySignal = -1; + d->properties[prop].setFlag(Notify, false); + } else if (d->properties[prop].notifySignal > index) + (d->properties[prop].notifySignal)--; + } + } +} + +/*! + Removes the constructor at \a index from this class. The indices of + all following constructors will be adjusted downwards by 1. + + \sa constructorCount(), addConstructor(), constructor() + \sa indexOfConstructor() +*/ +void QMetaObjectBuilder::removeConstructor(int index) +{ + if (index >= 0 && index < d->constructors.size()) + d->constructors.removeAt(index); +} + +/*! + Removes the property at \a index from this class. The indices of + all following properties will be adjusted downwards by 1. + + \sa propertyCount(), addProperty(), property(), indexOfProperty() +*/ +void QMetaObjectBuilder::removeProperty(int index) +{ + if (index >= 0 && index < d->properties.size()) + d->properties.removeAt(index); +} + +/*! + Removes the enumerator at \a index from this class. The indices of + all following enumerators will be adjusted downwards by 1. + + \sa enumertorCount(), addEnumerator(), enumerator() + \sa indexOfEnumerator() +*/ +void QMetaObjectBuilder::removeEnumerator(int index) +{ + if (index >= 0 && index < d->enumerators.size()) + d->enumerators.removeAt(index); +} + +/*! + Removes the item of class information at \a index from this class. + The indices of all following items will be adjusted downwards by 1. + + \sa classInfoCount(), addClassInfo(), classInfoName(), classInfoValue() + \sa indexOfClassInfo() +*/ +void QMetaObjectBuilder::removeClassInfo(int index) +{ + if (index >= 0 && index < d->classInfoNames.size()) { + d->classInfoNames.removeAt(index); + d->classInfoValues.removeAt(index); + } +} + +/*! + Removes the related meta object at \a index from this class. + The indices of all following related meta objects will be adjusted + downwards by 1. + + Related meta objects are used when resolving the enumerated type + associated with a property, where the enumerated type is in a + different class from the property. + + \sa relatedMetaObjectCount(), addRelatedMetaObject() + \sa relatedMetaObject() +*/ +void QMetaObjectBuilder::removeRelatedMetaObject(int index) +{ + if (index >= 0 && index < d->relatedMetaObjects.size()) + d->relatedMetaObjects.removeAt(index); +} + +/*! + Finds a method with the specified \a signature and returns its index; + otherwise returns -1. The \a signature will be normalized by this method. + + \sa method(), methodCount(), addMethod(), removeMethod() +*/ +int QMetaObjectBuilder::indexOfMethod(const QByteArray& signature) +{ + QByteArray sig = QMetaObject::normalizedSignature(signature); + for (int index = 0; index < d->methods.size(); ++index) { + if (sig == d->methods[index].signature) + return index; + } + return -1; +} + +/*! + Finds a signal with the specified \a signature and returns its index; + otherwise returns -1. The \a signature will be normalized by this method. + + \sa indexOfMethod(), indexOfSlot() +*/ +int QMetaObjectBuilder::indexOfSignal(const QByteArray& signature) +{ + QByteArray sig = QMetaObject::normalizedSignature(signature); + for (int index = 0; index < d->methods.size(); ++index) { + if (sig == d->methods[index].signature && + d->methods[index].methodType() == QMetaMethod::Signal) + return index; + } + return -1; +} + +/*! + Finds a slot with the specified \a signature and returns its index; + otherwise returns -1. The \a signature will be normalized by this method. + + \sa indexOfMethod(), indexOfSignal() +*/ +int QMetaObjectBuilder::indexOfSlot(const QByteArray& signature) +{ + QByteArray sig = QMetaObject::normalizedSignature(signature); + for (int index = 0; index < d->methods.size(); ++index) { + if (sig == d->methods[index].signature && + d->methods[index].methodType() == QMetaMethod::Slot) + return index; + } + return -1; +} + +/*! + Finds a constructor with the specified \a signature and returns its index; + otherwise returns -1. The \a signature will be normalized by this method. + + \sa constructor(), constructorCount(), addConstructor(), removeConstructor() +*/ +int QMetaObjectBuilder::indexOfConstructor(const QByteArray& signature) +{ + QByteArray sig = QMetaObject::normalizedSignature(signature); + for (int index = 0; index < d->constructors.size(); ++index) { + if (sig == d->constructors[index].signature) + return index; + } + return -1; +} + +/*! + Finds a property with the specified \a name and returns its index; + otherwise returns -1. + + \sa property(), propertyCount(), addProperty(), removeProperty() +*/ +int QMetaObjectBuilder::indexOfProperty(const QByteArray& name) +{ + for (int index = 0; index < d->properties.size(); ++index) { + if (name == d->properties[index].name) + return index; + } + return -1; +} + +/*! + Finds an enumerator with the specified \a name and returns its index; + otherwise returns -1. + + \sa enumertor(), enumeratorCount(), addEnumerator(), removeEnumerator() +*/ +int QMetaObjectBuilder::indexOfEnumerator(const QByteArray& name) +{ + for (int index = 0; index < d->enumerators.size(); ++index) { + if (name == d->enumerators[index].name) + return index; + } + return -1; +} + +/*! + Finds an item of class information with the specified \a name and + returns its index; otherwise returns -1. + + \sa classInfoName(), classInfoValue(), classInfoCount(), addClassInfo() + \sa removeClassInfo() +*/ +int QMetaObjectBuilder::indexOfClassInfo(const QByteArray& name) +{ + for (int index = 0; index < d->classInfoNames.size(); ++index) { + if (name == d->classInfoNames[index]) + return index; + } + return -1; +} + +// Align on a specific type boundary. +#define ALIGN(size,type) \ + (size) = ((size) + sizeof(type) - 1) & ~(sizeof(type) - 1) + +// Build a string into a QMetaObject representation. Returns the +// position in the string table where the string was placed. +static int buildString + (char *buf, char *str, int *offset, const QByteArray& value, int empty) +{ + if (value.size() == 0 && empty >= 0) + return empty; + if (buf) { + memcpy(str + *offset, value.constData(), value.size()); + str[*offset + value.size()] = '\0'; + } + int posn = *offset; + *offset += value.size() + 1; + return posn; +} + +// Build the parameter array string for a method. +static QByteArray buildParameterNames + (const QByteArray& signature, const QList<QByteArray>& parameterNames) +{ + // If the parameter name list is specified, then concatenate them. + if (!parameterNames.isEmpty()) { + QByteArray names; + bool first = true; + foreach (const QByteArray &name, parameterNames) { + if (first) + first = false; + else + names += (char)','; + names += name; + } + return names; + } + + // Count commas in the signature, excluding those inside template arguments. + int index = signature.indexOf('('); + if (index < 0) + return QByteArray(); + ++index; + if (index >= signature.size()) + return QByteArray(); + if (signature[index] == ')') + return QByteArray(); + int count = 1; + int brackets = 0; + while (index < signature.size() && signature[index] != ',') { + char ch = signature[index++]; + if (ch == '<') + ++brackets; + else if (ch == '>') + --brackets; + else if (ch == ',' && brackets <= 0) + ++count; + } + return QByteArray(count - 1, ','); +} + +// Build a QMetaObject in "buf" based on the information in "d". +// If "buf" is null, then return the number of bytes needed to +// build the QMetaObject. Returns -1 if the metaobject if +// relocatable is set, but the metaobject contains extradata. +static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, + bool relocatable) +{ + int size = 0; + int dataIndex; + int enumIndex; + int index; + bool hasNotifySignals = false; + + if (relocatable && + (d->relatedMetaObjects.size() > 0 || d->staticMetacallFunction)) + return -1; + + // Create the main QMetaObject structure at the start of the buffer. + QMetaObject *meta = reinterpret_cast<QMetaObject *>(buf); + size += sizeof(QMetaObject); + ALIGN(size, int); + if (buf) { + if (!relocatable) meta->d.superdata = d->superClass; + meta->d.extradata = 0; + } + + // Populate the QMetaObjectPrivate structure. + QMetaObjectPrivate *pmeta + = reinterpret_cast<QMetaObjectPrivate *>(buf + size); + int pmetaSize = size; + dataIndex = 13; // Number of fields in the QMetaObjectPrivate. + for (index = 0; index < d->properties.size(); ++index) { + if (d->properties[index].notifySignal != -1) { + hasNotifySignals = true; + break; + } + } + if (buf) { + pmeta->revision = 3; + pmeta->flags = d->flags; + pmeta->className = 0; // Class name is always the first string. + + pmeta->classInfoCount = d->classInfoNames.size(); + pmeta->classInfoData = dataIndex; + dataIndex += 2 * d->classInfoNames.size(); + + pmeta->methodCount = d->methods.size(); + pmeta->methodData = dataIndex; + dataIndex += 5 * d->methods.size(); + + pmeta->propertyCount = d->properties.size(); + pmeta->propertyData = dataIndex; + dataIndex += 3 * d->properties.size(); + if (hasNotifySignals) + dataIndex += d->properties.size(); + + pmeta->enumeratorCount = d->enumerators.size(); + pmeta->enumeratorData = dataIndex; + dataIndex += 4 * d->enumerators.size(); + + pmeta->constructorCount = d->constructors.size(); + pmeta->constructorData = dataIndex; + dataIndex += 5 * d->constructors.size(); + } else { + dataIndex += 2 * d->classInfoNames.size(); + dataIndex += 5 * d->methods.size(); + dataIndex += 3 * d->properties.size(); + if (hasNotifySignals) + dataIndex += d->properties.size(); + dataIndex += 4 * d->enumerators.size(); + dataIndex += 5 * d->constructors.size(); + } + + // Allocate space for the enumerator key names and values. + enumIndex = dataIndex; + for (index = 0; index < d->enumerators.size(); ++index) { + QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); + dataIndex += 2 * enumerator->keys.size(); + } + + // Zero terminator at the end of the data offset table. + ++dataIndex; + + // Find the start of the data and string tables. + int *data = reinterpret_cast<int *>(pmeta); + size += dataIndex * sizeof(int); + char *str = reinterpret_cast<char *>(buf + size); + if (buf) { + if (relocatable) { + meta->d.stringdata = reinterpret_cast<const char *>((quintptr)size); + meta->d.data = reinterpret_cast<uint *>((quintptr)pmetaSize); + } else { + meta->d.stringdata = str; + meta->d.data = reinterpret_cast<uint *>(data); + } + } + + // Reset the current data position to just past the QMetaObjectPrivate. + dataIndex = 13; + + // Add the class name to the string table. + int offset = 0; + buildString(buf, str, &offset, d->className, -1); + + // Add a common empty string, which is used to indicate "void" + // method returns, empty tag strings, etc. + int empty = buildString(buf, str, &offset, QByteArray(), -1); + + // Output the class infos, + for (index = 0; index < d->classInfoNames.size(); ++index) { + int name = buildString(buf, str, &offset, d->classInfoNames[index], empty); + int value = buildString(buf, str, &offset, d->classInfoValues[index], empty); + if (buf) { + data[dataIndex] = name; + data[dataIndex + 1] = value; + } + dataIndex += 2; + } + + // Output the methods in the class. + for (index = 0; index < d->methods.size(); ++index) { + QMetaMethodBuilderPrivate *method = &(d->methods[index]); + int sig = buildString(buf, str, &offset, method->signature, empty); + int params; + QByteArray names = buildParameterNames + (method->signature, method->parameterNames); + params = buildString(buf, str, &offset, names, empty); + int ret = buildString(buf, str, &offset, method->returnType, empty); + int tag = buildString(buf, str, &offset, method->tag, empty); + int attrs = method->attributes; + if (buf) { + data[dataIndex] = sig; + data[dataIndex + 1] = params; + data[dataIndex + 2] = ret; + data[dataIndex + 3] = tag; + data[dataIndex + 4] = attrs; + } + dataIndex += 5; + } + + // Output the properties in the class. + for (index = 0; index < d->properties.size(); ++index) { + QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); + int name = buildString(buf, str, &offset, prop->name, empty); + int type = buildString(buf, str, &offset, prop->type, empty); + int flags = prop->flags; + + if (!isVariantType(prop->type)) { + flags |= EnumOrFlag; + } else { + flags |= qvariant_nameToType(prop->type) << 24; + } + + if (buf) { + data[dataIndex] = name; + data[dataIndex + 1] = type; + data[dataIndex + 2] = flags; + } + dataIndex += 3; + } + if (hasNotifySignals) { + for (index = 0; index < d->properties.size(); ++index) { + QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); + if (buf) { + if (prop->notifySignal != -1) + data[dataIndex] = prop->notifySignal; + else + data[dataIndex] = 0; + } + ++dataIndex; + } + } + + // Output the enumerators in the class. + for (index = 0; index < d->enumerators.size(); ++index) { + QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); + int name = buildString(buf, str, &offset, enumerator->name, empty); + int isFlag = (int)(enumerator->isFlag); + int count = enumerator->keys.size(); + int enumOffset = enumIndex; + if (buf) { + data[dataIndex] = name; + data[dataIndex + 1] = isFlag; + data[dataIndex + 2] = count; + data[dataIndex + 3] = enumOffset; + } + for (int key = 0; key < count; ++key) { + int keyIndex = buildString(buf, str, &offset, enumerator->keys[key], empty); + if (buf) { + data[enumOffset++] = keyIndex; + data[enumOffset++] = enumerator->values[key]; + } + } + dataIndex += 4; + enumIndex += 2 * count; + } + + // Output the constructors in the class. + for (index = 0; index < d->constructors.size(); ++index) { + QMetaMethodBuilderPrivate *method = &(d->constructors[index]); + int sig = buildString(buf, str, &offset, method->signature, empty); + int params; + QByteArray names = buildParameterNames + (method->signature, method->parameterNames); + params = buildString(buf, str, &offset, names, empty); + int ret = buildString(buf, str, &offset, method->returnType, empty); + int tag = buildString(buf, str, &offset, method->tag, empty); + int attrs = method->attributes; + if (buf) { + data[dataIndex] = sig; + data[dataIndex + 1] = params; + data[dataIndex + 2] = ret; + data[dataIndex + 3] = tag; + data[dataIndex + 4] = attrs; + } + dataIndex += 5; + } + + // One more empty string to act as a terminator. + buildString(buf, str, &offset, QByteArray(), -1); + size += offset; + + // Output the zero terminator in the data array. + if (buf) + data[enumIndex] = 0; + + // Create the extradata block if we need one. + if (d->relatedMetaObjects.size() > 0 || d->staticMetacallFunction) { + ALIGN(size, QMetaObject **); + ALIGN(size, QMetaObjectBuilder::StaticMetacallFunction); + QMetaObjectExtraData *extra = + reinterpret_cast<QMetaObjectExtraData *>(buf + size); + size += sizeof(QMetaObjectExtraData); + ALIGN(size, QMetaObject *); +#ifdef Q_NO_DATA_RELOCATION + QMetaObjectAccessor *objects = + reinterpret_cast<QMetaObjectAccessor *>(buf + size); +#else + const QMetaObject **objects = + reinterpret_cast<const QMetaObject **>(buf + size); +#endif + if (buf) { + if (d->relatedMetaObjects.size() > 0) { + extra->objects = objects; + for (index = 0; index < d->relatedMetaObjects.size(); ++index) + objects[index] = d->relatedMetaObjects[index]; + objects[index] = 0; + } else { + extra->objects = 0; + } + extra->static_metacall = d->staticMetacallFunction; + meta->d.extradata = reinterpret_cast<void *>(extra); + } + if (d->relatedMetaObjects.size() > 0) + size += sizeof(QMetaObject *) * (d->relatedMetaObjects.size() + 1); + } + + // Align the final size and return it. + ALIGN(size, void *); + return size; +} + +/*! + Converts this meta object builder into a concrete QMetaObject. + The return value should be deallocated using qFree() once it + is no longer needed. + + The returned meta object is a snapshot of the state of the + QMetaObjectBuilder. Any further modifications to the QMetaObjectBuilder + will not be reflected in previous meta objects returned by + this method. +*/ +QMetaObject *QMetaObjectBuilder::toMetaObject() const +{ + int size = buildMetaObject(d, 0, false); + char *buf = reinterpret_cast<char *>(qMalloc(size)); + buildMetaObject(d, buf, false); + return reinterpret_cast<QMetaObject *>(buf); +} + +/* + \internal + + Converts this meta object builder into relocatable data. This data can + be stored, copied and later passed to fromRelocatableData() to create a + concrete QMetaObject. + + The data is specific to the architecture on which it was created, but is not + specific to the process that created it. Not all meta object builder's can + be converted to data in this way. If \a ok is provided, it will be set to + true if the conversion succeeds, and false otherwise. If a + staticMetacallFunction() or any relatedMetaObject()'s are specified the + conversion to relocatable data will fail. +*/ +QByteArray QMetaObjectBuilder::toRelocatableData(bool *ok) const +{ + int size = buildMetaObject(d, 0, true); + if (size == -1) { + if (ok) *ok = false; + return QByteArray(); + } + + QByteArray data; + data.resize(size); + char *buf = data.data(); + buildMetaObject(d, buf, true); + if (ok) *ok = true; + return data; +} + +/* + \internal + + Sets the \a data returned from toRelocatableData() onto a concrete + QMetaObject instance, \a output. As the meta object's super class is not + saved in the relocatable data, it must be passed as \a superClass. +*/ +void QMetaObjectBuilder::fromRelocatableData(QMetaObject *output, + const QMetaObject *superclass, + const QByteArray &data) +{ + if (!output) + return; + + const char *buf = data.constData(); + const QMetaObject *dataMo = reinterpret_cast<const QMetaObject *>(buf); + + quintptr stringdataOffset = (quintptr)dataMo->d.stringdata; + quintptr dataOffset = (quintptr)dataMo->d.data; + + output->d.superdata = superclass; + output->d.stringdata = buf + stringdataOffset; + output->d.data = reinterpret_cast<const uint *>(buf + dataOffset); +} + +/*! + \typedef QMetaObjectBuilder::StaticMetacallFunction + + Typedef for static metacall functions. The three parameters are + the call type value, the constructor index, and the + array of parameters. +*/ + +/*! + Returns the static metacall function to use to construct objects + of this class. The default value is null. + + \sa setStaticMetacallFunction() +*/ +QMetaObjectBuilder::StaticMetacallFunction QMetaObjectBuilder::staticMetacallFunction() const +{ + return d->staticMetacallFunction; +} + +/*! + Sets the static metacall function to use to construct objects + of this class to \a value. The default value is null. + + \sa staticMetacallFunction() +*/ +void QMetaObjectBuilder::setStaticMetacallFunction + (QMetaObjectBuilder::StaticMetacallFunction value) +{ + d->staticMetacallFunction = value; +} + +#ifndef QT_NO_DATASTREAM + +/*! + Serializes the contents of the meta object builder onto \a stream. + + \sa deserialize() +*/ +void QMetaObjectBuilder::serialize(QDataStream& stream) const +{ + int index; + + // Write the class and super class names. + stream << d->className; + if (d->superClass) + stream << QByteArray(d->superClass->className()); + else + stream << QByteArray(); + + // Write the counts for each type of class member. + stream << d->classInfoNames.size(); + stream << d->methods.size(); + stream << d->properties.size(); + stream << d->enumerators.size(); + stream << d->constructors.size(); + stream << d->relatedMetaObjects.size(); + + // Write the items of class information. + for (index = 0; index < d->classInfoNames.size(); ++index) { + stream << d->classInfoNames[index]; + stream << d->classInfoValues[index]; + } + + // Write the methods. + for (index = 0; index < d->methods.size(); ++index) { + const QMetaMethodBuilderPrivate *method = &(d->methods[index]); + stream << method->signature; + stream << method->returnType; + stream << method->parameterNames; + stream << method->tag; + stream << method->attributes; + } + + // Write the properties. + for (index = 0; index < d->properties.size(); ++index) { + const QMetaPropertyBuilderPrivate *property = &(d->properties[index]); + stream << property->name; + stream << property->type; + stream << property->flags; + stream << property->notifySignal; + } + + // Write the enumerators. + for (index = 0; index < d->enumerators.size(); ++index) { + const QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); + stream << enumerator->name; + stream << enumerator->isFlag; + stream << enumerator->keys; + stream << enumerator->values; + } + + // Write the constructors. + for (index = 0; index < d->constructors.size(); ++index) { + const QMetaMethodBuilderPrivate *method = &(d->constructors[index]); + stream << method->signature; + stream << method->returnType; + stream << method->parameterNames; + stream << method->tag; + stream << method->attributes; + } + + // Write the related meta objects. +#ifdef Q_NO_DATA_RELOCATION + //the related meta objects will be function pointers + //which you have to add to the builder manually. + //e.g. + //builder2.addRelatedMetaObject(QLocale::getStaticMetaObject); +#else + for (index = 0; index < d->relatedMetaObjects.size(); ++index) { + const QMetaObject *meta = d->relatedMetaObjects[index]; + stream << QByteArray(meta->className()); + } +#endif + + // Add an extra empty QByteArray for additional data in future versions. + // This should help maintain backwards compatibility, allowing older + // versions to read newer data. + stream << QByteArray(); +} + +// Resolve a class name using the name reference map. +static const QMetaObject *resolveClassName + (const QMap<QByteArray, const QMetaObject *>& references, + const QByteArray& name) +{ + if (name == QByteArray("QObject")) + return &QObject::staticMetaObject; + else + return references.value(name, 0); +} + +/*! + Deserializes a meta object builder from \a stream into + this meta object builder. + + The \a references parameter specifies a mapping from class names + to QMetaObject instances for resolving the super class name and + related meta objects in the object that is deserialized. + The meta object for QObject is implicitly added to \a references + and does not need to be supplied. + + The QDataStream::status() value on \a stream will be set to + QDataStream::ReadCorruptData if the input data is corrupt. + The status will be set to QDataStream::ReadPastEnd if the + input was exhausted before the full meta object was read. + + \sa serialize() +*/ +void QMetaObjectBuilder::deserialize + (QDataStream& stream, + const QMap<QByteArray, const QMetaObject *>& references) +{ + QByteArray name; + const QMetaObject *cl; + int index; + + // Clear all members in the builder to their default states. + d->className.clear(); + d->superClass = &QObject::staticMetaObject; + d->classInfoNames.clear(); + d->classInfoValues.clear(); + d->methods.clear(); + d->properties.clear(); + d->enumerators.clear(); + d->constructors.clear(); + d->relatedMetaObjects.clear(); + d->staticMetacallFunction = 0; + + // Read the class and super class names. + stream >> d->className; + stream >> name; + if (name.isEmpty()) { + d->superClass = 0; + } else if ((cl = resolveClassName(references, name)) != 0) { + d->superClass = cl; + } else { + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + + // Read the counts for each type of class member. + int classInfoCount, methodCount, propertyCount; + int enumeratorCount, constructorCount, relatedMetaObjectCount; + stream >> classInfoCount; + stream >> methodCount; + stream >> propertyCount; + stream >> enumeratorCount; + stream >> constructorCount; + stream >> relatedMetaObjectCount; + if (classInfoCount < 0 || methodCount < 0 || + propertyCount < 0 || enumeratorCount < 0 || + constructorCount < 0 || relatedMetaObjectCount < 0) { + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + + // Read the items of class information. + for (index = 0; index < classInfoCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + QByteArray value; + stream >> name; + stream >> value; + addClassInfo(name, value); + } + + // Read the member methods. + for (index = 0; index < methodCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + stream >> name; + addMethod(name); + QMetaMethodBuilderPrivate *method = &(d->methods[index]); + stream >> method->returnType; + stream >> method->parameterNames; + stream >> method->tag; + stream >> method->attributes; + if (method->methodType() == QMetaMethod::Constructor) { + // Cannot add a constructor in this set of methods. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + } + + // Read the properties. + for (index = 0; index < propertyCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + QByteArray type; + stream >> name; + stream >> type; + addProperty(name, type); + QMetaPropertyBuilderPrivate *property = &(d->properties[index]); + stream >> property->flags; + stream >> property->notifySignal; + if (property->notifySignal < -1 || + property->notifySignal >= d->methods.size()) { + // Notify signal method index is out of range. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + if (property->notifySignal >= 0 && + d->methods[property->notifySignal].methodType() != QMetaMethod::Signal) { + // Notify signal method index does not refer to a signal. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + } + + // Read the enumerators. + for (index = 0; index < enumeratorCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + stream >> name; + addEnumerator(name); + QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); + stream >> enumerator->isFlag; + stream >> enumerator->keys; + stream >> enumerator->values; + if (enumerator->keys.size() != enumerator->values.size()) { + // Mismatch between number of keys and number of values. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + } + + // Read the constructor methods. + for (index = 0; index < constructorCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + stream >> name; + addConstructor(name); + QMetaMethodBuilderPrivate *method = &(d->constructors[index]); + stream >> method->returnType; + stream >> method->parameterNames; + stream >> method->tag; + stream >> method->attributes; + if (method->methodType() != QMetaMethod::Constructor) { + // The type must be Constructor. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + } + + // Read the related meta objects. +#ifdef Q_NO_DATA_RELOCATION + //the related meta objects will be function pointers + //which you have to add to the builder manually. + //e.g. + //builder2.addRelatedMetaObject(QLocale::getStaticMetaObject); +#else + for (index = 0; index < relatedMetaObjectCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + stream >> name; + cl = resolveClassName(references, name); + if (!cl) { + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + addRelatedMetaObject(cl); + } +#endif + + // Read the extra data block, which is reserved for future use. + stream >> name; +} + +#endif // !QT_NO_DATASTREAM + +/*! + \class QMetaMethodBuilder + \internal + \brief The QMetaMethodBuilder class enables modifications to a method definition on a meta object builder. +*/ + +QMetaMethodBuilderPrivate *QMetaMethodBuilder::d_func() const +{ + // Positive indices indicate methods, negative indices indicate constructors. + if (_mobj && _index >= 0 && _index < _mobj->d->methods.size()) + return &(_mobj->d->methods[_index]); + else if (_mobj && -_index >= 1 && -_index <= _mobj->d->constructors.size()) + return &(_mobj->d->constructors[(-_index) - 1]); + else + return 0; +} + +/*! + \fn QMetaMethodBuilder::QMetaMethodBuilder() + \internal +*/ + +/*! + Returns the index of this method within its QMetaObjectBuilder. +*/ +int QMetaMethodBuilder::index() const +{ + if (_index >= 0) + return _index; // Method, signal, or slot + else + return (-_index) - 1; // Constructor +} + +/*! + Returns the type of this method (signal, slot, method, or constructor). +*/ +QMetaMethod::MethodType QMetaMethodBuilder::methodType() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->methodType(); + else + return QMetaMethod::Method; +} + +/*! + Returns the signature of this method. + + \sa parameterNames(), returnType() +*/ +QByteArray QMetaMethodBuilder::signature() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->signature; + else + return QByteArray(); +} + +/*! + Returns the return type for this method; empty if the method's + return type is \c{void}. + + \sa setReturnType(), signature() +*/ +QByteArray QMetaMethodBuilder::returnType() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->returnType; + else + return QByteArray(); +} + +/*! + Sets the return type for this method to \a value. If \a value + is empty, then the method's return type is \c{void}. The \a value + will be normalized before it is added to the method. + + \sa returnType(), signature() +*/ +void QMetaMethodBuilder::setReturnType(const QByteArray& value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + d->returnType = QMetaObject::normalizedType(value); +} + +/*! + Returns the list of parameter names for this method. + + \sa setParameterNames() +*/ +QList<QByteArray> QMetaMethodBuilder::parameterNames() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->parameterNames; + else + return QList<QByteArray>(); +} + +/*! + Sets the list of parameter names for this method to \a value. + + \sa parameterNames() +*/ +void QMetaMethodBuilder::setParameterNames(const QList<QByteArray>& value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + d->parameterNames = value; +} + +/*! + Returns the tag associated with this method. + + \sa setTag() +*/ +QByteArray QMetaMethodBuilder::tag() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->tag; + else + return QByteArray(); +} + +/*! + Sets the tag associated with this method to \a value. + + \sa setTag() +*/ +void QMetaMethodBuilder::setTag(const QByteArray& value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + d->tag = value; +} + +/*! + Returns the access specification of this method (private, protected, + or public). The default value is QMetaMethod::Public for methods, + slots, and constructors. The default value is QMetaMethod::Protected + for signals. + + \sa setAccess() +*/ +QMetaMethod::Access QMetaMethodBuilder::access() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->access(); + else + return QMetaMethod::Public; +} + +/*! + Sets the access specification of this method (private, protected, + or public) to \a value. If the method is a signal, this function + will be ignored. + + \sa access() +*/ +void QMetaMethodBuilder::setAccess(QMetaMethod::Access value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d && d->methodType() != QMetaMethod::Signal) + d->setAccess(value); +} + +/*! + Returns the additional attributes for this method. + + \sa setAttributes() +*/ +int QMetaMethodBuilder::attributes() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return (d->attributes >> 4); + else + return 0; +} + +/*! + Sets the additional attributes for this method to \a value. + + \sa attributes() +*/ +void QMetaMethodBuilder::setAttributes(int value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + d->attributes = ((d->attributes & 0x0f) | (value << 4)); +} + +/*! + \class QMetaPropertyBuilder + \internal + \brief The QMetaPropertyBuilder class enables modifications to a property definition on a meta object builder. +*/ + +QMetaPropertyBuilderPrivate *QMetaPropertyBuilder::d_func() const +{ + if (_mobj && _index >= 0 && _index < _mobj->d->properties.size()) + return &(_mobj->d->properties[_index]); + else + return 0; +} + +/*! + \fn QMetaPropertyBuilder::QMetaPropertyBuilder() + \internal +*/ + +/*! + \fn int QMetaPropertyBuilder::index() const + + Returns the index of this property within its QMetaObjectBuilder. +*/ + +/*! + Returns the name associated with this property. + + \sa type() +*/ +QByteArray QMetaPropertyBuilder::name() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->name; + else + return QByteArray(); +} + +/*! + Returns the type associated with this property. + + \sa name() +*/ +QByteArray QMetaPropertyBuilder::type() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->type; + else + return QByteArray(); +} + +/*! + Returns true if this property has a notify signal; false otherwise. + + \sa notifySignal(), setNotifySignal(), removeNotifySignal() +*/ +bool QMetaPropertyBuilder::hasNotifySignal() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Notify); + else + return false; +} + +/*! + Returns the notify signal associated with this property. + + \sa hasNotifySignal(), setNotifySignal(), removeNotifySignal() +*/ +QMetaMethodBuilder QMetaPropertyBuilder::notifySignal() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d && d->notifySignal >= 0) + return QMetaMethodBuilder(_mobj, d->notifySignal); + else + return QMetaMethodBuilder(); +} + +/*! + Sets the notify signal associated with this property to \a value. + + \sa hasNotifySignal(), notifySignal(), removeNotifySignal() +*/ +void QMetaPropertyBuilder::setNotifySignal(const QMetaMethodBuilder& value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) { + if (value._mobj) { + d->notifySignal = value._index; + d->setFlag(Notify, true); + } else { + d->notifySignal = -1; + d->setFlag(Notify, false); + } + } +} + +/*! + Removes the notify signal from this property. + + \sa hasNotifySignal(), notifySignal(), setNotifySignal() +*/ +void QMetaPropertyBuilder::removeNotifySignal() +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) { + d->notifySignal = -1; + d->setFlag(Notify, false); + } +} + +/*! + Returns true if this property is readable; otherwise returns false. + The default value is true. + + \sa setReadable(), isWritable() +*/ +bool QMetaPropertyBuilder::isReadable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Readable); + else + return false; +} + +/*! + Returns true if this property is writable; otherwise returns false. + The default value is true. + + \sa setWritable(), isReadable() +*/ +bool QMetaPropertyBuilder::isWritable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Writable); + else + return false; +} + +/*! + Returns true if this property can be reset to a default value; otherwise + returns false. The default value is false. + + \sa setResettable() +*/ +bool QMetaPropertyBuilder::isResettable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Resettable); + else + return false; +} + +/*! + Returns true if this property is designable; otherwise returns false. + This default value is false. + + \sa setDesignable(), isScriptable(), isStored() +*/ +bool QMetaPropertyBuilder::isDesignable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Designable); + else + return false; +} + +/*! + Returns true if the property is scriptable; otherwise returns false. + This default value is true. + + \sa setScriptable(), isDesignable(), isStored() +*/ +bool QMetaPropertyBuilder::isScriptable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Scriptable); + else + return false; +} + +/*! + Returns true if the property is stored; otherwise returns false. + This default value is false. + + \sa setStored(), isDesignable(), isScriptable() +*/ +bool QMetaPropertyBuilder::isStored() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Stored); + else + return false; +} + +/*! + Returns true if the property is editable; otherwise returns false. + This default value is false. + + \sa setEditable(), isDesignable(), isScriptable(), isStored() +*/ +bool QMetaPropertyBuilder::isEditable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Editable); + else + return false; +} + +/*! + Returns true if this property is designated as the \c USER + property, i.e., the one that the user can edit or that is + significant in some other way. Otherwise it returns + false. This default value is false. + + \sa setUser(), isDesignable(), isScriptable() +*/ +bool QMetaPropertyBuilder::isUser() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(User); + else + return false; +} + +/*! + Returns true if the property has a C++ setter function that + follows Qt's standard "name" / "setName" pattern. Designer and uic + query hasStdCppSet() in order to avoid expensive + QObject::setProperty() calls. All properties in Qt [should] follow + this pattern. The default value is false. + + \sa setStdCppSet() +*/ +bool QMetaPropertyBuilder::hasStdCppSet() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(StdCppSet); + else + return false; +} + +/*! + Returns true if the property is an enumerator or flag type; + otherwise returns false. This default value is false. + + \sa setEnumOrFlag() +*/ +bool QMetaPropertyBuilder::isEnumOrFlag() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(EnumOrFlag); + else + return false; +} + +/*! + Returns true if the property is constant; otherwise returns false. + The default value is false. +*/ +bool QMetaPropertyBuilder::isConstant() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Constant); + else + return false; +} + +/*! + Returns true if the property is final; otherwise returns false. + The default value is false. +*/ +bool QMetaPropertyBuilder::isFinal() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Final); + else + return false; +} + +/*! + Sets this property to readable if \a value is true. + + \sa isReadable(), setWritable() +*/ +void QMetaPropertyBuilder::setReadable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Readable, value); +} + +/*! + Sets this property to writable if \a value is true. + + \sa isWritable(), setReadable() +*/ +void QMetaPropertyBuilder::setWritable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Writable, value); +} + +/*! + Sets this property to resettable if \a value is true. + + \sa isResettable() +*/ +void QMetaPropertyBuilder::setResettable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Resettable, value); +} + +/*! + Sets this property to designable if \a value is true. + + \sa isDesignable(), setScriptable(), setStored() +*/ +void QMetaPropertyBuilder::setDesignable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Designable, value); +} + +/*! + Sets this property to scriptable if \a value is true. + + \sa isScriptable(), setDesignable(), setStored() +*/ +void QMetaPropertyBuilder::setScriptable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Scriptable, value); +} + +/*! + Sets this property to storable if \a value is true. + + \sa isStored(), setDesignable(), setScriptable() +*/ +void QMetaPropertyBuilder::setStored(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Stored, value); +} + +/*! + Sets this property to editable if \a value is true. + + \sa isEditable(), setDesignable(), setScriptable(), setStored() +*/ +void QMetaPropertyBuilder::setEditable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Editable, value); +} + +/*! + Sets the \c USER flag on this property to \a value. + + \sa isUser(), setDesignable(), setScriptable() +*/ +void QMetaPropertyBuilder::setUser(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(User, value); +} + +/*! + Sets the C++ setter flag on this property to \a value, which is + true if the property has a C++ setter function that follows Qt's + standard "name" / "setName" pattern. + + \sa hasStdCppSet() +*/ +void QMetaPropertyBuilder::setStdCppSet(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(StdCppSet, value); +} + +/*! + Sets this property to be of an enumerator or flag type if + \a value is true. + + \sa isEnumOrFlag() +*/ +void QMetaPropertyBuilder::setEnumOrFlag(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(EnumOrFlag, value); +} + +/*! + Sets the \c CONSTANT flag on this property to \a value. + + \sa isConstant() +*/ +void QMetaPropertyBuilder::setConstant(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Constant, value); +} + +/*! + Sets the \c FINAL flag on this property to \a value. + + \sa isFinal() +*/ +void QMetaPropertyBuilder::setFinal(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Final, value); +} + + +/*! + \class QMetaEnumBuilder + \internal + \brief The QMetaEnumBuilder class enables modifications to an enumerator definition on a meta object builder. +*/ + +QMetaEnumBuilderPrivate *QMetaEnumBuilder::d_func() const +{ + if (_mobj && _index >= 0 && _index < _mobj->d->enumerators.size()) + return &(_mobj->d->enumerators[_index]); + else + return 0; +} + +/*! + \fn QMetaEnumBuilder::QMetaEnumBuilder() + \internal +*/ + +/*! + \fn int QMetaEnumBuilder::index() const + + Returns the index of this enumerator within its QMetaObjectBuilder. +*/ + +/*! + Returns the name of the enumerator (without the scope). +*/ +QByteArray QMetaEnumBuilder::name() const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + return d->name; + else + return QByteArray(); +} + +/*! + Returns true if this enumerator is used as a flag; otherwise returns + false. + + \sa setIsFlag() +*/ +bool QMetaEnumBuilder::isFlag() const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + return d->isFlag; + else + return false; +} + +/*! + Sets this enumerator to be used as a flag if \a value is true. + + \sa isFlag() +*/ +void QMetaEnumBuilder::setIsFlag(bool value) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + d->isFlag = value; +} + +/*! + Returns the number of keys. + + \sa key(), addKey() +*/ +int QMetaEnumBuilder::keyCount() const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + return d->keys.size(); + else + return 0; +} + +/*! + Returns the key with the given \a index, or an empty QByteArray + if no such key exists. + + \sa keyCount(), addKey(), value() +*/ +QByteArray QMetaEnumBuilder::key(int index) const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d && index >= 0 && index < d->keys.size()) + return d->keys[index]; + else + return QByteArray(); +} + +/*! + Returns the value with the given \a index; or returns -1 if there + is no such value. + + \sa keyCount(), addKey(), key() +*/ +int QMetaEnumBuilder::value(int index) const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d && index >= 0 && index < d->keys.size()) + return d->values[index]; + else + return -1; +} + +/*! + Adds a new key called \a name to this enumerator, associated + with \a value. Returns the index of the new key. + + \sa keyCount(), key(), value(), removeKey() +*/ +int QMetaEnumBuilder::addKey(const QByteArray& name, int value) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) { + int index = d->keys.size(); + d->keys += name; + d->values += value; + return index; + } else { + return -1; + } +} + +/*! + Removes the key at \a index from this enumerator. + + \sa addKey() +*/ +void QMetaEnumBuilder::removeKey(int index) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d && index >= 0 && index < d->keys.size()) { + d->keys.removeAt(index); + d->values.removeAt(index); + } +} + +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/qmetaobjectbuilder_p.h b/src/serviceframework/ipc/qmetaobjectbuilder_p.h new file mode 100644 index 00000000..b123f2c9 --- /dev/null +++ b/src/serviceframework/ipc/qmetaobjectbuilder_p.h @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMETAOBJECTBUILDER_H +#define QMETAOBJECTBUILDER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of moc. This header file may change from version to version without notice, +// or even be removed. +// +// We mean it. +// + +#include <qserviceframeworkglobal.h> +#include <QtCore/qobject.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qmap.h> + +QTM_BEGIN_NAMESPACE + +class QMetaObjectBuilderPrivate; +class QMetaMethodBuilder; +class QMetaMethodBuilderPrivate; +class QMetaPropertyBuilder; +class QMetaPropertyBuilderPrivate; +class QMetaEnumBuilder; +class QMetaEnumBuilderPrivate; + +typedef const QMetaObject& (*QMetaObjectAccessor)(); + +#ifdef IGNORE_METAOBJECTBUILDER_EXPORT + class QMetaObjectBuilder +#else + class Q_AUTOTEST_EXPORT QMetaObjectBuilder +#endif +{ +public: + enum AddMember + { + ClassName = 0x00000001, + SuperClass = 0x00000002, + Methods = 0x00000004, + Signals = 0x00000008, + Slots = 0x00000010, + Constructors = 0x00000020, + Properties = 0x00000040, + Enumerators = 0x00000080, + ClassInfos = 0x00000100, + RelatedMetaObjects = 0x00000200, + StaticMetacall = 0x00000400, + PublicMethods = 0x00000800, + ProtectedMethods = 0x00001000, + PrivateMethods = 0x00002000, + AllMembers = 0x7FFFFFFF, + AllPrimaryMembers = 0x7FFFFBFC + }; + Q_DECLARE_FLAGS(AddMembers, AddMember) + + enum MetaObjectFlag { + DynamicMetaObject = 0x01 + }; + Q_DECLARE_FLAGS(MetaObjectFlags, MetaObjectFlag) + + QMetaObjectBuilder(); + explicit QMetaObjectBuilder(const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members = AllMembers); + virtual ~QMetaObjectBuilder(); + + QByteArray className() const; + void setClassName(const QByteArray& name); + + const QMetaObject *superClass() const; + void setSuperClass(const QMetaObject *meta); + + MetaObjectFlags flags() const; + void setFlags(MetaObjectFlags); + + int methodCount() const; + int constructorCount() const; + int propertyCount() const; + int enumeratorCount() const; + int classInfoCount() const; + int relatedMetaObjectCount() const; + + QMetaMethodBuilder addMethod(const QByteArray& signature); + QMetaMethodBuilder addMethod(const QByteArray& signature, const QByteArray& returnType); + QMetaMethodBuilder addMethod(const QMetaMethod& prototype); + + QMetaMethodBuilder addSlot(const QByteArray& signature); + QMetaMethodBuilder addSignal(const QByteArray& signature); + + QMetaMethodBuilder addConstructor(const QByteArray& signature); + QMetaMethodBuilder addConstructor(const QMetaMethod& prototype); + + QMetaPropertyBuilder addProperty(const QByteArray& name, const QByteArray& type, int notifierId=-1); + QMetaPropertyBuilder addProperty(const QMetaProperty& prototype); + + QMetaEnumBuilder addEnumerator(const QByteArray& name); + QMetaEnumBuilder addEnumerator(const QMetaEnum& prototype); + + int addClassInfo(const QByteArray& name, const QByteArray& value); + +#ifdef Q_NO_DATA_RELOCATION + int addRelatedMetaObject(const QMetaObjectAccessor &meta); +#else + int addRelatedMetaObject(const QMetaObject *meta); +#endif + + void addMetaObject(const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members = AllMembers); + + QMetaMethodBuilder method(int index) const; + QMetaMethodBuilder constructor(int index) const; + QMetaPropertyBuilder property(int index) const; + QMetaEnumBuilder enumerator(int index) const; + const QMetaObject *relatedMetaObject(int index) const; + + QByteArray classInfoName(int index) const; + QByteArray classInfoValue(int index) const; + + void removeMethod(int index); + void removeConstructor(int index); + void removeProperty(int index); + void removeEnumerator(int index); + void removeClassInfo(int index); + void removeRelatedMetaObject(int index); + + int indexOfMethod(const QByteArray& signature); + int indexOfSignal(const QByteArray& signature); + int indexOfSlot(const QByteArray& signature); + int indexOfConstructor(const QByteArray& signature); + int indexOfProperty(const QByteArray& name); + int indexOfEnumerator(const QByteArray& name); + int indexOfClassInfo(const QByteArray& name); + + typedef QMetaObjectExtraData::StaticMetacallFunction StaticMetacallFunction; + + QMetaObjectBuilder::StaticMetacallFunction staticMetacallFunction() const; + void setStaticMetacallFunction(QMetaObjectBuilder::StaticMetacallFunction value); + + QMetaObject *toMetaObject() const; + QByteArray toRelocatableData(bool * = 0) const; + static void fromRelocatableData(QMetaObject *, const QMetaObject *, const QByteArray &); + +#ifndef QT_NO_DATASTREAM + void serialize(QDataStream& stream) const; + void deserialize + (QDataStream& stream, + const QMap<QByteArray, const QMetaObject *>& references); +#endif + +private: + Q_DISABLE_COPY(QMetaObjectBuilder) + + QMetaObjectBuilderPrivate *d; + + friend class QMetaMethodBuilder; + friend class QMetaPropertyBuilder; + friend class QMetaEnumBuilder; +}; + +#ifdef IGNORE_METAOBJECTBUILDER_EXPORT + class QMetaMethodBuilder +#else + class Q_AUTOTEST_EXPORT QMetaMethodBuilder +#endif +{ +public: + QMetaMethodBuilder() : _mobj(0), _index(0) {} + + int index() const; + + QMetaMethod::MethodType methodType() const; + QByteArray signature() const; + + QByteArray returnType() const; + void setReturnType(const QByteArray& value); + + QList<QByteArray> parameterNames() const; + void setParameterNames(const QList<QByteArray>& value); + + QByteArray tag() const; + void setTag(const QByteArray& value); + + QMetaMethod::Access access() const; + void setAccess(QMetaMethod::Access value); + + int attributes() const; + void setAttributes(int value); + +private: + const QMetaObjectBuilder *_mobj; + int _index; + + friend class QMetaObjectBuilder; + friend class QMetaPropertyBuilder; + + QMetaMethodBuilder(const QMetaObjectBuilder *mobj, int index) + : _mobj(mobj), _index(index) {} + + QMetaMethodBuilderPrivate *d_func() const; +}; + +#ifdef IGNORE_METAOBJECTBUILDER_EXPORT + class QMetaPropertyBuilder +#else + class Q_AUTOTEST_EXPORT QMetaPropertyBuilder +#endif +{ +public: + QMetaPropertyBuilder() : _mobj(0), _index(0) {} + + int index() const { return _index; } + + QByteArray name() const; + QByteArray type() const; + + bool hasNotifySignal() const; + QMetaMethodBuilder notifySignal() const; + void setNotifySignal(const QMetaMethodBuilder& value); + void removeNotifySignal(); + + bool isReadable() const; + bool isWritable() const; + bool isResettable() const; + bool isDesignable() const; + bool isScriptable() const; + bool isStored() const; + bool isEditable() const; + bool isUser() const; + bool hasStdCppSet() const; + bool isEnumOrFlag() const; + bool isConstant() const; + bool isFinal() const; + + void setReadable(bool value); + void setWritable(bool value); + void setResettable(bool value); + void setDesignable(bool value); + void setScriptable(bool value); + void setStored(bool value); + void setEditable(bool value); + void setUser(bool value); + void setStdCppSet(bool value); + void setEnumOrFlag(bool value); + void setConstant(bool value); + void setFinal(bool value); + +private: + const QMetaObjectBuilder *_mobj; + int _index; + + friend class QMetaObjectBuilder; + + QMetaPropertyBuilder(const QMetaObjectBuilder *mobj, int index) + : _mobj(mobj), _index(index) {} + + QMetaPropertyBuilderPrivate *d_func() const; +}; + +#ifdef IGNORE_METAOBJECTBUILDER_EXPORT + class QMetaEnumBuilder +#else + class Q_AUTOTEST_EXPORT QMetaEnumBuilder +#endif +{ +public: + QMetaEnumBuilder() : _mobj(0), _index(0) {} + + int index() const { return _index; } + + QByteArray name() const; + + bool isFlag() const; + void setIsFlag(bool value); + + int keyCount() const; + QByteArray key(int index) const; + int value(int index) const; + + int addKey(const QByteArray& name, int value); + void removeKey(int index); + +private: + const QMetaObjectBuilder *_mobj; + int _index; + + friend class QMetaObjectBuilder; + + QMetaEnumBuilder(const QMetaObjectBuilder *mobj, int index) + : _mobj(mobj), _index(index) {} + + QMetaEnumBuilderPrivate *d_func() const; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::AddMembers) +Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::MetaObjectFlags) + +QTM_END_NAMESPACE + +#endif diff --git a/src/serviceframework/ipc/qremoteserviceregister_dbus_p.cpp b/src/serviceframework/ipc/qremoteserviceregister_dbus_p.cpp new file mode 100644 index 00000000..3097a3e3 --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_dbus_p.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteserviceregister_p.h" +#include "qremoteserviceregister_dbus_p.h" + +#include <QDataStream> +#include <QTimer> + + +QTM_BEGIN_NAMESPACE + +class DBusEndPoint : public QServiceIpcEndPoint +{ + Q_OBJECT + +public: + DBusEndPoint(QDBusInterface* iface, int type, QObject* parent = 0) + : QServiceIpcEndPoint(parent), interface(iface), endType(type) + { + Q_ASSERT(interface); + interface->setParent(this); + connect(interface, SIGNAL(packageReceived(QByteArray,int,QString)), + this, SLOT(readPackage(QByteArray,int,QString))); + + if (endType == CLIENT) { + QDBusServiceWatcher *watcher = new QDBusServiceWatcher(interface->service(), + interface->connection(), + QDBusServiceWatcher::WatchForUnregistration); + + QObject::connect(watcher, SIGNAL(serviceUnregistered(QString)), + this, SLOT(serviceRemoved(QString))); + } + } + + ~DBusEndPoint() + { + } + +public slots: + void closeIncoming() + { + QDBusMessage msg = interface->callWithArgumentList(QDBus::AutoDetect, QLatin1String("closeIncoming"), + QList<QVariant>() << instanceId); + } + + void setInstanceId(const QString& id) + { + instanceId = id; + } + +Q_SIGNALS: + void ipcFault(QService::UnrecoverableIPCError); + +protected: + void flushPackage(const QServicePackage& package) + { + if (!QDBusConnection::sessionBus().isConnected()) { + qWarning() << "Cannot connect to DBus"; + } + + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_6); + out << package; + + packageId = package.d->messageId; + interface->asyncCall(QLatin1String("writePackage"), block, endType, packageId); + } + +protected slots: + void readPackage(const QByteArray &package, int type, const QString &id) { + // Check that its of a client-server nature + if (endType != type) { + // Client to Server + if (type != SERVER) { + readIncoming(package); + } else { + // Server to Client + if (id == packageId) { + readIncoming(package); + } + } + } + } + + void readIncoming(const QByteArray &package) + { + QDataStream data(package); + QServicePackage pack; + data >> pack; + + incoming.enqueue(pack); + emit readyRead(); + } + + void serviceRemoved(const QString& name) + { + Q_UNUSED(name); + QString serviceName = interface->service(); + QDBusReply<bool> reply = interface->connection().interface()->isServiceRegistered(serviceName); + if (!reply.value()) { + emit ipcFault(QService::ErrorServiceNoLongerAvailable); + } + } + +private: + QDBusInterface* interface; + QString packageId; + int endType; + QString instanceId; +}; + +class DBusSessionAdaptor: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.nokia.qtmobility.sfw.DBusSession") + +public: + DBusSessionAdaptor(QObject *parent); + ~DBusSessionAdaptor() {} + +public slots: + QByteArray writePackage(const QByteArray &package, int type, const QString &id) { + QByteArray ret; + QMetaObject::invokeMethod(parent(), "writePackage", + Q_RETURN_ARG(QByteArray, ret), + Q_ARG(QByteArray, package), + Q_ARG(int, type), + Q_ARG(QString, id)); + return ret; + } + + bool processIncoming() { + bool ret; + QMetaObject::invokeMethod(parent(), "processIncoming", + Q_RETURN_ARG(bool, ret)); + return ret; + } + + void acceptIncoming(bool accept) { + QMetaObject::invokeMethod(parent(), "acceptIncoming", + Q_ARG(bool, accept)); + } + + void closeIncoming(const QString& instanceId) { + QMetaObject::invokeMethod(parent(), "closeIncoming", + Q_ARG(QString, instanceId)); + } + +signals: + void packageReceived(const QByteArray &package, int type, const QString &id); + void newConnection(int pid, int uid); +}; + +DBusSessionAdaptor::DBusSessionAdaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) +{ + setAutoRelaySignals(true); +} + +QRemoteServiceRegisterDBusPrivate::QRemoteServiceRegisterDBusPrivate(QObject* parent) + : QRemoteServiceRegisterPrivate(parent) +{ +} + +QRemoteServiceRegisterDBusPrivate::~QRemoteServiceRegisterDBusPrivate() +{ +} + +void QRemoteServiceRegisterDBusPrivate::publishServices(const QString& ident) +{ + if (!createServiceEndPoint(ident)) + QTimer::singleShot(0, QCoreApplication::instance(), SLOT(quit())); +} + +/*! + Creates endpoint on service side. +*/ +bool QRemoteServiceRegisterDBusPrivate::createServiceEndPoint(const QString& ident) +{ + int endPoints = 0; + + InstanceManager *iManager = InstanceManager::instance(); + QList<QRemoteServiceRegister::Entry> list = iManager->allEntries(); + + if (list.size() < 1) + return false; + + QDBusConnection connection = QDBusConnection::sessionBus(); + if (!connection.isConnected()) { + qWarning() << "Cannot connect to DBus"; + return 0; + } + + // Registers the service and session object on DBus if needed + for (int i=0; i<list.size(); i++) { + QString serviceName = "com.nokia.qtmobility.sfw." + list[i].serviceName(); + QDBusReply<bool> reply = connection.interface()->isServiceRegistered(serviceName); + if (reply.value()) + continue; + + if (!connection.registerService(serviceName)) { + qWarning() << "Cannot register service to DBus:" << serviceName; + continue; + } + + // Create and register our DBusSession server/client + session = new DBusSession(this); + new DBusSessionAdaptor(session); + QObject::connect(session, SIGNAL(newConnection(int,int)), + this, SLOT(processIncoming(int,int))); + + QString path = "/" + list[i].interfaceName() + "/" + ident; + path.replace(QLatin1String("."), QLatin1String("/")); + if (!connection.objectRegisteredAt(path)) { + if (!connection.registerObject(path, session)) { + qWarning() << "Cannot register service session to DBus:" << path; + continue; + } + + iface = new QDBusInterface(serviceName, path, QLatin1String(""), QDBusConnection::sessionBus()); + if (!iface->isValid()) { + qWarning() << "Cannot connect to remote service" << serviceName << path;; + continue; + } + + DBusEndPoint* ipcEndPoint = new DBusEndPoint(iface, SERVER); + ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Service, ipcEndPoint, this); + + // Connect session process disconnections + QObject::connect(session, SIGNAL(closeConnection(QString,QString)), + endPoint, SLOT(disconnected(QString,QString))); + + endPoints++; + } + } + + if (endPoints > 0) + return true; + + return false; +} + +void QRemoteServiceRegisterDBusPrivate::processIncoming(int pid, int uid) +{ + if (getSecurityFilter()) { + QRemoteServiceRegisterCredentials cred; + cred.fd = -1; + cred.pid = pid; + cred.uid = uid; + cred.gid = -1; + + if (!getSecurityFilter()(reinterpret_cast<const void *>(&cred))) { + session->acceptIncoming(false); + + // Close service if no instances + if (quitOnLastInstanceClosed() && + InstanceManager::instance()->totalInstances() < 1) + QCoreApplication::exit(); + + return; + } + } + + session->acceptIncoming(true); +} + +QRemoteServiceRegisterPrivate* QRemoteServiceRegisterPrivate::constructPrivateObject(QObject *parent) +{ + return new QRemoteServiceRegisterDBusPrivate(parent); +} + +/*! + Creates endpoint on client side. +*/ +QObject* QRemoteServiceRegisterPrivate::proxyForService(const QRemoteServiceRegister::Entry& entry, const QString& location) +{ + const QString serviceName = "com.nokia.qtmobility.sfw." + entry.serviceName(); + QString path = "/" + entry.interfaceName() + "/" + location; + path.replace(QLatin1String("."), QLatin1String("/")); + + QDBusConnection connection = QDBusConnection::sessionBus(); + if (!connection.isConnected()) { + qWarning() << "Cannot connect to DBus"; + return 0; + } + + // Dummy call to autostart the service if not running + connection.call(QDBusMessage::createMethodCall(serviceName, path, QLatin1String(""), QLatin1String("q_autostart"))); + + QDBusInterface *iface = new QDBusInterface(serviceName, path, QLatin1String(""), QDBusConnection::sessionBus()); + if (!iface->isValid()) { + qWarning() << "Cannot connect to remote service" << serviceName << path; + return 0; + } + + QDBusReply<bool> reply = iface->call(QDBus::Block, QLatin1String("processIncoming")); + if (reply.value()) { + DBusEndPoint* ipcEndPoint = new DBusEndPoint(iface, CLIENT); + ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Client, ipcEndPoint); + + QObject *proxy = endPoint->constructProxy(entry); + ipcEndPoint->setInstanceId(endPoint->getInstanceId()); + + if (proxy) { + QObject::connect(proxy, SIGNAL(destroyed()), endPoint, SLOT(deleteLater())); + QObject::connect(proxy, SIGNAL(destroyed()), ipcEndPoint, SLOT(closeIncoming())); + QObject::connect(ipcEndPoint, SIGNAL(ipcFault(QService::UnrecoverableIPCError)), + proxy, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError))); + } + return proxy; + } + + qDebug() << "Insufficient credentials to load a service instance"; + return 0; +} + +#include "moc_qremoteserviceregister_dbus_p.cpp" +#include "qremoteserviceregister_dbus_p.moc" +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/qremoteserviceregister_dbus_p.h b/src/serviceframework/ipc/qremoteserviceregister_dbus_p.h new file mode 100644 index 00000000..50e88139 --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_dbus_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTESERVICEREGISTER_DBUS_P_H +#define QREMOTESERVICEREGISTER_DBUS_P_H + +#include <QUuid> +#include <QtDBus/QtDBus> + +#include "qremoteserviceregister.h" +#include "instancemanager_p.h" +#include "qserviceinterfacedescriptor.h" +#include "ipcendpoint_p.h" +#include "objectendpoint_dbus_p.h" + +QTM_BEGIN_NAMESPACE + +#define SERVER 0 +#define CLIENT 1 + +class ObjectEndPoint; + +class DBusSession: public QObject, protected QDBusContext +{ + Q_OBJECT +public: + DBusSession(QObject* parent = 0) + : QObject(parent) + {} + ~DBusSession() {} + +public slots: + QByteArray writePackage(const QByteArray &package, int type, const QString &id) { + + QDataStream data(package); + QServicePackage pack; + data >> pack; + + if (type == CLIENT && pack.d->packageType == 0) { + // Use the client DBus connection as the Id + QDBusReply<QString> reply = + connection().interface()->serviceOwner(message().service()); + QString clientId = reply.value(); + pack.d->payload = QVariant(clientId); + + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_6); + out << pack; + + emit packageReceived(block, type, id); + return block; + } + + emit packageReceived(package, type, id); + return package; + } + + bool processIncoming() { + int pid = connection().interface()->servicePid(message().service()); + int uid = connection().interface()->serviceUid(message().service()); + emit newConnection(pid, uid); + return m_accept; + } + + void acceptIncoming(bool accept) { + m_accept = accept; + } + + void closeIncoming(const QString& instanceId) { + QDBusReply<QString> reply = + connection().interface()->serviceOwner(message().service()); + const QString& clientId = reply.value(); + emit closeConnection(clientId, instanceId); + } + +Q_SIGNALS: + void packageReceived(const QByteArray &package, int type, const QString &id); + void newConnection(int pid, int uid); + void closeConnection(const QString& clientId, const QString& instanceId); + +private: + bool m_accept; +}; + + +class QRemoteServiceRegisterDBusPrivate: public QRemoteServiceRegisterPrivate +{ + Q_OBJECT +public: + QRemoteServiceRegisterDBusPrivate(QObject* parent); + ~QRemoteServiceRegisterDBusPrivate(); + void publishServices(const QString& ident ); + +public slots: + void processIncoming(int pid, int uid); + +private: + bool createServiceEndPoint(const QString& ident); + + QList<ObjectEndPoint*> pendingConnections; + QDBusInterface *iface; + DBusSession *session; + QDBusConnection *connection; +}; + +QTM_END_NAMESPACE + +#endif diff --git a/src/serviceframework/ipc/qremoteserviceregister_ls_p.cpp b/src/serviceframework/ipc/qremoteserviceregister_ls_p.cpp new file mode 100644 index 00000000..6af7d344 --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_ls_p.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteserviceregister_p.h" +#include "qremoteserviceregister_ls_p.h" +#include "ipcendpoint_p.h" +#include "objectendpoint_p.h" + +#include <QLocalServer> +#include <QLocalSocket> +#include <QDataStream> +#include <QTimer> +#include <QProcess> +#include <QFile> + +#include <time.h> +#include <sys/types.h> /* See NOTES */ + +#ifndef Q_OS_WIN +#include <sys/un.h> +#include <sys/socket.h> +#else +// Needed for ::Sleep, while we wait for a better solution +#include <Windows.h> +#include <Winbase.h> +#endif + +#ifdef LOCAL_PEERCRED /* from sys/un.h */ +#include <sys/ucred.h> +#endif + +QTM_BEGIN_NAMESPACE + +//IPC based on QLocalSocket + +class LocalSocketEndPoint : public QServiceIpcEndPoint +{ + Q_OBJECT +public: + LocalSocketEndPoint(QLocalSocket* s, QObject* parent = 0) + : QServiceIpcEndPoint(parent), socket(s) + { + Q_ASSERT(socket); + socket->setParent(this); + connect(s, SIGNAL(readyRead()), this, SLOT(readIncoming())); + connect(s, SIGNAL(disconnected()), this, SIGNAL(disconnected())); + connect(s, SIGNAL(disconnected()), this, SLOT(ipcfault())); + + if (socket->bytesAvailable()) + QTimer::singleShot(0, this, SLOT(readIncoming())); + } + + ~LocalSocketEndPoint() + { + disconnect(this, SLOT(ipcfault())); + socket->close(); + } + +Q_SIGNALS: + void errorUnrecoverableIPCFault(QService::UnrecoverableIPCError); + + +protected: + void flushPackage(const QServicePackage& package) + { + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_6); + out << package; + socket->write(block); + } + +protected slots: + void readIncoming() + { + QDataStream in(socket); + in.setVersion(QDataStream::Qt_4_6); + + while (socket->bytesAvailable()) { + QServicePackage package; + in >> package; + incoming.enqueue(package); + } + + emit readyRead(); + } + void ipcfault() + { + emit errorUnrecoverableIPCFault(QService::ErrorServiceNoLongerAvailable); + } + +private: + QLocalSocket* socket; +}; + +QRemoteServiceRegisterLocalSocketPrivate::QRemoteServiceRegisterLocalSocketPrivate(QObject* parent) + : QRemoteServiceRegisterPrivate(parent) +{ +} + +void QRemoteServiceRegisterLocalSocketPrivate::publishServices( const QString& ident) +{ + createServiceEndPoint(ident) ; +} + +void QRemoteServiceRegisterLocalSocketPrivate::processIncoming() +{ + if (localServer->hasPendingConnections()) { + QLocalSocket* s = localServer->nextPendingConnection(); + //LocalSocketEndPoint owns socket + int fd = s->socketDescriptor(); + if (getSecurityFilter()){ + QRemoteServiceRegisterCredentials qcred; + memset(&qcred, 0, sizeof(QRemoteServiceRegisterCredentials)); + qcred.fd = fd; + +#if defined(LOCAL_PEERCRED) + struct xucred xuc; + socklen_t len = sizeof(struct xucred); + + if (getsockopt(fd, SOL_SOCKET, LOCAL_PEERCRED, &xuc, &len) == 0) { + qcred.pid = -1; // No PID on bsd + qcred.uid = xuc.cr_uid; + qcred.gid = xuc.cr_gid; + + } + +#elif defined(SO_PEERCRED) + struct ucred uc; + socklen_t len = sizeof(struct ucred); + + if( getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) { + qcred.pid = uc.pid; + qcred.uid = uc.uid; + qcred.gid = uc.gid; + } + else { + s->close(); + perror("Failed to get peer credential"); + return; + } +#else + s->close(); + qWarning("Credentials check unsupprted on this platform"); + return; +#endif + qDebug() << "Security filter call"; + if (!getSecurityFilter()(reinterpret_cast<const void *>(&qcred))){ + s->close(); + return; + } + } + LocalSocketEndPoint* ipcEndPoint = new LocalSocketEndPoint(s); + ObjectEndPoint* endpoint = new ObjectEndPoint(ObjectEndPoint::Service, ipcEndPoint, this); + Q_UNUSED(endpoint); + } +} + +/* + Creates endpoint on service side. +*/ +bool QRemoteServiceRegisterLocalSocketPrivate::createServiceEndPoint(const QString& ident) +{ + //other IPC mechanisms such as dbus may have to publish the + //meta object definition for all registered service types + QLocalServer::removeServer(ident); + localServer = new QLocalServer(this); + if ( !localServer->listen(ident) ) { + qWarning() << "Cannot create local socket endpoint"; + return false; + } + connect(localServer, SIGNAL(newConnection()), this, SLOT(processIncoming())); + if (localServer->hasPendingConnections()) + QTimer::singleShot(0, this, SLOT(processIncoming())); + + return true; +} + +QRemoteServiceRegisterPrivate* QRemoteServiceRegisterPrivate::constructPrivateObject(QObject *parent) +{ + return new QRemoteServiceRegisterLocalSocketPrivate(parent); +} + +/* + Creates endpoint on client side. +*/ +QObject* QRemoteServiceRegisterPrivate::proxyForService(const QRemoteServiceRegister::Entry& entry, const QString& location) +{ + QLocalSocket* socket = new QLocalSocket(); + socket->connectToServer(location); + if (!socket->waitForConnected()){ + if (!socket->isValid()) { + QString path = location; + qWarning() << "Cannot connect to remote service, trying to start service " << path; + // If we have autotests enable, check for the service in . +#ifdef QTM_BUILD_UNITTESTS + QFile file("./" + path); + if (file.exists()){ + path.prepend("./"); + } +#endif + qint64 pid = 0; + // Start the service as a detached process + if (QProcess::startDetached(path, QStringList(), QString(), &pid)){ + int i; + socket->connectToServer(location); + for (i = 0; !socket->isValid() && i < 1000; i++){ + // Temporary hack till we can improve startup signaling +#ifdef Q_OS_WIN + ::Sleep(10); +#else + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = 1000000; + nanosleep(&tm, 0x0); +#endif + socket->connectToServer(location); + // keep trying for a while + } + if (!socket->isValid()){ + qWarning() << "Server failed to start within waiting period"; + return false; + } + } + else + qWarning() << "Server could not be started"; + } + } + if (socket->isValid()){ + LocalSocketEndPoint* ipcEndPoint = new LocalSocketEndPoint(socket); + ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Client, ipcEndPoint); + + QObject *proxy = endPoint->constructProxy(entry); + if (proxy){ + QObject::connect(proxy, SIGNAL(destroyed()), endPoint, SLOT(deleteLater())); + QObject::connect(ipcEndPoint, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), + proxy, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError))); + } + ipcEndPoint->setParent(proxy); + endPoint->setParent(proxy); + return proxy; + } + return 0; +} + +#include "moc_qremoteserviceregister_ls_p.cpp" +#include "qremoteserviceregister_ls_p.moc" +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/qremoteserviceregister_ls_p.h b/src/serviceframework/ipc/qremoteserviceregister_ls_p.h new file mode 100644 index 00000000..356df496 --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_ls_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTESERVICEREGISTER_LS_P_H +#define QREMOTESERVICEREGISTER_LS_P_H + +#include "qremoteserviceregister.h" +#include "instancemanager_p.h" +#include "qserviceinterfacedescriptor.h" +#include "qremoteserviceregister_p.h" +#include <QLocalServer> + +QTM_BEGIN_NAMESPACE + +class ObjectEndPoint; + +class QRemoteServiceRegisterLocalSocketPrivate: public QRemoteServiceRegisterPrivate +{ + Q_OBJECT +public: + QRemoteServiceRegisterLocalSocketPrivate(QObject* parent); + void publishServices(const QString& ident ); + +public slots: + void processIncoming(); + +private: + bool createServiceEndPoint(const QString& ident); + + QLocalServer* localServer; + QList<ObjectEndPoint*> pendingConnections; +}; + +QTM_END_NAMESPACE + +#endif diff --git a/src/serviceframework/ipc/qremoteserviceregister_p.cpp b/src/serviceframework/ipc/qremoteserviceregister_p.cpp new file mode 100644 index 00000000..a4b5b196 --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_p.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteserviceregister_p.h" +#include "instancemanager_p.h" + +#include <QCoreApplication> + +QTM_BEGIN_NAMESPACE + +QRemoteServiceRegisterPrivate::QRemoteServiceRegisterPrivate(QObject* parent) + : QObject(parent), iFilter(0) +{ + setQuitOnLastInstanceClosed(true); +} + +QRemoteServiceRegisterPrivate::~QRemoteServiceRegisterPrivate() +{ +} + +//void QRemoteServiceRegisterPrivate::publishServices( const QString& ident) +//{ +// qWarning("QRemoteServiceregisterPrivate::publishServices has not been reimplemented"); +//} +// +//void QRemoteServiceRegisterPrivate::processIncoming() +//{ +// qWarning("QRemoteServiceRegisterPrivate::processIncoming has not been reimplemented"); +//} + +bool QRemoteServiceRegisterPrivate::quitOnLastInstanceClosed() const +{ + return m_quit; +} + +void QRemoteServiceRegisterPrivate::setQuitOnLastInstanceClosed(bool quit) +{ + m_quit = quit; + if (m_quit) { + connect(InstanceManager::instance(), SIGNAL(allInstancesClosed()), QCoreApplication::instance(), SLOT(quit())); + } + else { + disconnect(InstanceManager::instance(), SIGNAL(allInstancesClosed()), QCoreApplication::instance(), SLOT(quit())); + } +} + +QRemoteServiceRegister::SecurityFilter QRemoteServiceRegisterPrivate::setSecurityFilter(QRemoteServiceRegister::SecurityFilter filter) +{ + QRemoteServiceRegister::SecurityFilter f; + f = filter; + iFilter = filter; + return f; +} + +QRemoteServiceRegister::SecurityFilter QRemoteServiceRegisterPrivate::getSecurityFilter() +{ + return iFilter; +} + + +#include "moc_qremoteserviceregister_p.cpp" +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/qremoteserviceregister_p.h b/src/serviceframework/ipc/qremoteserviceregister_p.h new file mode 100644 index 00000000..0c7e493f --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTESERVICEREGISTER_P_H +#define QREMOTESERVICEREGISTER_P_H + +#include "qremoteserviceregister.h" +#include "instancemanager_p.h" +#include "qserviceinterfacedescriptor.h" + +QTM_BEGIN_NAMESPACE + +class ObjectEndPoint; +class QRemoteServiceRegisterPrivate: public QObject +{ + Q_OBJECT + Q_PROPERTY(bool quitOnLastInstanceClosed READ quitOnLastInstanceClosed WRITE setQuitOnLastInstanceClosed) +public: + QRemoteServiceRegisterPrivate(QObject* parent); + virtual ~QRemoteServiceRegisterPrivate(); + + virtual void publishServices(const QString& ident ) = 0; + + virtual bool quitOnLastInstanceClosed() const; + virtual void setQuitOnLastInstanceClosed(const bool quit); + + virtual QRemoteServiceRegister::SecurityFilter setSecurityFilter(QRemoteServiceRegister::SecurityFilter filter); + +public slots: + // Must be implemented in the subclass + //void processIncoming(); + +protected: + virtual QRemoteServiceRegister::SecurityFilter getSecurityFilter(); + +private: + bool m_quit; + QRemoteServiceRegister::SecurityFilter iFilter; + +public: + static QObject* proxyForService(const QRemoteServiceRegister::Entry& entry, const QString& location); + static QRemoteServiceRegisterPrivate* constructPrivateObject(QObject *parent); +}; + +QTM_END_NAMESPACE + +#endif diff --git a/src/serviceframework/ipc/qremoteserviceregister_s60.cpp b/src/serviceframework/ipc/qremoteserviceregister_s60.cpp new file mode 100644 index 00000000..969af3a3 --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_s60.cpp @@ -0,0 +1,874 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteserviceregister_s60_p.h" +#include "ipcendpoint_p.h" +#include "objectendpoint_p.h" +#include <QTimer> +#include <QCoreApplication> + +#include <e32base.h> + +/* IPC based on Symbian Client-Server framework + * This module implements the Symbian specific IPC mechanisms and related control. + * IPC is based on Symbian Client-Server architecture. + */ + +QTM_BEGIN_NAMESPACE + +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG +void printServicePackage(const QServicePackage& package) +{ + if(package.d) { + qDebug() << "QServicePackage packageType : " << package.d->packageType; + qDebug() << "QServicePackage QUuid : " << package.d->messageId; + qDebug() << "QServicePackage responseType : " << package.d->responseType; + qDebug() << "QServicePackage value : " << package.d->payload; + } + else { + qDebug() << "Invalid ServicePackage" << " LEAVING"; + User::Leave(KErrCorrupt); + } +} +#endif + + +class SymbianClientEndPoint: public QServiceIpcEndPoint +{ + Q_OBJECT +public: + SymbianClientEndPoint(RServiceSession* session, QObject* parent = 0) + : QServiceIpcEndPoint(parent), session(session) + { + Q_ASSERT(session); +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "Symbian IPC endpoint created. 255 buffer"; +#endif + connect(session, SIGNAL(Disconnected()), this, SIGNAL(disconnected())); + } + + ~SymbianClientEndPoint() + { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "Symbian IPC client endpoint destroyed."; +#endif + } + + void PackageReceived(QServicePackage package) + { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "SymbianClientEndPoint::PackageReceived. Enqueueing and emiting ReadyRead()"; + printServicePackage(package); +#endif + incoming.enqueue(package); + emit readyRead(); + } + +protected: + void flushPackage(const QServicePackage& package) + { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "SymbianClientEndPoint::flushPackage() for package: "; + printServicePackage(package); +#endif + session->SendServicePackage(package); + } + +private: + RServiceSession *session; +}; + +class SymbianServerEndPoint: public QServiceIpcEndPoint +{ + Q_OBJECT +public: + SymbianServerEndPoint(CServiceProviderServerSession* session, QObject* parent = 0) + : QServiceIpcEndPoint(parent), session(session), obj(0) + { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "Symbian IPC server endpoint created."; +#endif + Q_ASSERT(session); + // CBase derived classes cannot inherit from QObject, + // hence manual ownershipping instead of Qt hierarchy. + session->SetParent(this); + } + + + ~SymbianServerEndPoint() + { + #ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "Symbian IPC server endpoint destroyed. --- emit disconnected"; + #endif + + emit disconnected(); + } + + void packageReceived(QServicePackage package) + { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "SymbianServerEndPoint::packageReceived, putting to queue and emiting readyread."; + printServicePackage(package); +#endif + incoming.enqueue(package); + emit readyRead(); + } + + void setObjectEndPoint(ObjectEndPoint *aObj) + { + obj = aObj; + } + +protected: + void flushPackage(const QServicePackage& package) + { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "SymbianServerEndPoint::flushPackage() for package: "; + printServicePackage(package); +#endif + TRAPD(err, session->SendServicePackageL(package)); + if(err != KErrNone){ + qDebug() << "flushPackage: Failed to send request: " << err; + } + } + +private: + CServiceProviderServerSession *session; + ObjectEndPoint *obj; +}; + +QRemoteServiceRegisterSymbianPrivate::QRemoteServiceRegisterSymbianPrivate(QObject *parent) + : QRemoteServiceRegisterPrivate(parent), m_server(0) +{ +} + +QRemoteServiceRegisterSymbianPrivate::~QRemoteServiceRegisterSymbianPrivate() +{ + delete m_server; +} + +void QRemoteServiceRegisterSymbianPrivate::publishServices(const QString &ident) +{ + // Create service side of the Symbian Client-Server architecture. + m_server = new CServiceProviderServer(this); + TPtrC serviceIdent(reinterpret_cast<const TUint16*>(ident.utf16())); + + if(getSecurityFilter()) + m_server->setSecurityFilter(getSecurityFilter()); + + TInt err = m_server->Start(serviceIdent); +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + if (err != KErrNone) { + qDebug() << "publishServices: server->Start() failed: " << err; + } else { + qDebug("publishServices: service started successfully"); + } +#endif + // If we're started by the client, notify them we're running + RProcess::Rendezvous(KErrNone); +} + +void QRemoteServiceRegisterSymbianPrivate::processIncoming(CServiceProviderServerSession* newSession) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("processingIncoming session creation."); +#endif + // Create service provider-side endpoints. + SymbianServerEndPoint* ipcEndPoint = new SymbianServerEndPoint(newSession, this); + ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Service, ipcEndPoint, this); + ipcEndPoint->setObjectEndPoint(endPoint); +} + +QRemoteServiceRegister::SecurityFilter QRemoteServiceRegisterSymbianPrivate::setSecurityFilter(QRemoteServiceRegister::SecurityFilter filter) +{ + if(m_server) + m_server->setSecurityFilter(filter); + + return QRemoteServiceRegisterPrivate::setSecurityFilter(filter); +} + + +QRemoteServiceRegisterPrivate* QRemoteServiceRegisterPrivate::constructPrivateObject(QObject *parent) +{ + return new QRemoteServiceRegisterSymbianPrivate(parent); +} + +QObject* QRemoteServiceRegisterPrivate::proxyForService(const QRemoteServiceRegister::Entry &entry, const QString &location) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "proxyForService for location: " << location; +#endif + // Create client-side session for the IPC and connect it to the service + // provide. If service provider is not up, it will be started. + // Connecting is tried few times in a loop, because if service starting is + // done at device startup, everything may not be ready yet. + RServiceSession *session = new RServiceSession(location); + int err = session->Connect(); + int i = 0; + while (err != KErrNone) { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "proxyForService Connecting in loop: " << i; +#endif + if (i > 10) { + qWarning() << "QtSFW failed to connect to service provider."; + return NULL; + } + User::After(50); + err = session->Connect(); + i++; + } + + // Create IPC endpoint. In practice binds the communication session and abstracting + // class presenting the IPC endoint. + SymbianClientEndPoint* ipcEndPoint = new SymbianClientEndPoint(session); + ipcEndPoint->setParent(session); + // Create an active message solicitor, which listens messages from server + ServiceMessageListener* messageListener = new ServiceMessageListener(session, ipcEndPoint); + // Create object endpoint, which handles the metaobject protocol. + ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Client, ipcEndPoint); + endPoint->setParent(session); + QObject *proxy = endPoint->constructProxy(entry); + session->setParent(proxy); + QObject::connect(session, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), + proxy, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError))); + return proxy; +} + +RServiceSession::RServiceSession(QString address) +: iSize(0), iListener(0), iDataSizes(KIpcBufferMinimumSize),iServerStarted(EFalse) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "RServiceSession() for address: " << address; +#endif + qt_symbian_throwIfError(iMessageFromServer.Create(KIpcBufferMinimumSize)); + iServerAddress = address; +} + +RServiceSession::~RServiceSession() +{ + delete iListener; + Close(); + iMessageFromServer.Close(); +} + +void RServiceSession::setListener(ServiceMessageListener *listener) +{ + iListener = listener; +} + +void RServiceSession::Close() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "RServiceSession close()"; +#endif + RSessionBase::Close(); +} + +TInt RServiceSession::Connect() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "RServiceSession Connect()"; +#endif + TInt err=KErrUnknown; + if(!iServerStarted){ + TInt err = StartServer(); + if (err == KErrNone) { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "StartServer successful, Creating session."; +#endif + iServerStarted = ETrue; + } + } + TPtrC serviceAddressPtr(reinterpret_cast<const TUint16*>(iServerAddress.utf16())); + err = CreateSession(serviceAddressPtr, Version()); + + return err; +} + +TVersion RServiceSession::Version() const +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "RServiceSession Version()"; +#endif + return TVersion(KServerMajorVersionNumber, KServerMinorVersionNumber, KServerBuildVersionNumber); +} + +void RServiceSession::SendServicePackage(const QServicePackage& aPackage) +{ + // Serialize the package into byte array, wrap it in descriptor, + // and send to service provider. + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_6); + out << aPackage; +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "SendServicePackage: Size of package sent from client to server: " << block.count(); +#endif + TPtrC8 ptr8((TUint8*)(block.constData()), block.size()); + TIpcArgs args(&ptr8, &iError); + TInt err = SendReceive(EServicePackage, args); +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "SendServicePackage error code received: " << err; +#endif + if(err != KErrNone){ + enum QService::UnrecoverableIPCError e = QService::ErrorUnknown; + switch(err){ + case KErrServerTerminated: + e = QService::ErrorServiceNoLongerAvailable; + break; + case KErrNoMemory: + case KErrServerBusy: // if the slots are full, something is really wrong + e = QService::ErrorOutofMemory; + break; + } + emit errorUnrecoverableIPCFault(e); + } +} + +void RServiceSession::addDataSize(TInt dataSize) +{ + iDataSizes.addSample(dataSize); +} + +// StartServer() checks if the service is already published by someone (i.e. can be found +// from Kernel side). If not, it will start the process that provides the service. +TInt RServiceSession::StartServer() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "RServiceSession::StartServer()"; +#endif + TInt ret = KErrNone; + TPtrC serviceAddressPtr(reinterpret_cast<const TUint16*>(iServerAddress.utf16())); + TFindServer findServer(serviceAddressPtr); + TFullName name; + // Looks up from Kernel-side if there are active servers with the given name. + // If not found, a process providing the service needs to be started. + if (findServer.Next(name) != KErrNone) { +#if defined(__WINS__) && !defined(SYMBIAN_EMULATOR_SUPPORTS_PERPROCESS_WSD) + qWarning("WINS Support for QSFW OOP not implemented."); +#else +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "RServiceSession::StartServer() Service not found. Starting " << iServerAddress; +#endif + TRequestStatus status; + RProcess serviceServerProcess; + ret = serviceServerProcess.Create(serviceAddressPtr, KNullDesC); + if (ret != KErrNone) { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "StartServer RProcess::Create failed: " << ret; +#endif + return ret; + } + // Point of synchronization. Waits until the started process calls + // counterpart of this function (quits wait also if process dies / panics). + serviceServerProcess.Rendezvous(status); + + if (status != KRequestPending) { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "StartServer Service Server Process Rendezvous() failed, killing process."; +#endif + serviceServerProcess.Kill(KErrNone); + serviceServerProcess.Close(); + return KErrGeneral; + } else { +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "StartServer Service Server Process Rendezvous() successful, resuming process."; +#endif + serviceServerProcess.Resume(); + } + User::WaitForRequest(status); + if (status != KErrNone){ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("RTR Process Resume failed."); +#endif + serviceServerProcess.Close(); + return status.Int(); + } + // Free the handle to the process (RHandleBase function, does not 'close process') + serviceServerProcess.Close(); +#endif // __WINS__ + } else { + qDebug() << "RServiceSession::StartServer() GTR Service found from Kernel, no need to start process."; + } + return ret; +} + +/* Since the average size will not change in the middle of a fragmented message + * trasnfer actual updates to buffer size will happen only when a new message + * arrives */ +void RServiceSession::updateIpcBufferSize() +{ + TInt weightedAvg = iDataSizes.averageWeighted(); + +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "updateIpcBufferSize(): current weighted average of data size is: "<<weightedAvg; + qDebug() << "Current IPC Buffer size is: "<<iMessageFromServer.MaxLength(); +#endif + TInt newSize = iMessageFromServer.MaxLength(); + if(weightedAvg > iMessageFromServer.MaxLength()){ + newSize = iMessageFromServer.MaxLength()*2; + } + else if(weightedAvg < iMessageFromServer.MaxLength()/2){ + + newSize = iMessageFromServer.MaxLength()/2; +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "updateIpcBufferSize(): current weighted average of data size is:(2) "<<weightedAvg<<" max/2"; +#endif + } + + if(newSize < KIpcBufferMinimumSize) + newSize = KIpcBufferMinimumSize; + else if(newSize > KIpcBufferMaximumSize) + newSize = KIpcBufferMaximumSize; + + + if(newSize != iMessageFromServer.MaxLength()) { + + iMessageFromServer.SetLength(0); + //If realloc fails the old descriptor is left unchanged. + iMessageFromServer.ReAlloc(newSize); +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "updateIpcBufferSize(): New Size of IPC Buffer: "<<newSize; +#endif + } +} + +void RServiceSession::ListenForPackages(TRequestStatus& aStatus) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "GTR RServiceSession::ListenForPackages(), iSize: " << iSize(); +#endif + updateIpcBufferSize(); + iArgs.Set(0, &iMessageFromServer); + // Total Size of returned messaage,which might differ from the amount of data in iMessageFromServer + iArgs.Set(1, &iSize); + iArgs.Set(2, &iError); + + SendReceive(EPackageRequest, iArgs, aStatus); +} + +void RServiceSession::CancelListenForPackages() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("RServiceSession::CancelListenForPackages -- 2"); +#endif + TInt err = SendReceive(EPackageRequestCancel, TIpcArgs(NULL)); + if(err != KErrNone){ + enum QService::UnrecoverableIPCError e = QService::ErrorUnknown; + switch(err){ + case KErrServerTerminated: + e = QService::ErrorServiceNoLongerAvailable; + break; + case KErrNoMemory: + case KErrServerBusy: // if the slots are full, something is really wrong + e = QService::ErrorOutofMemory; + break; + } + emit errorUnrecoverableIPCFault(e); + } +} + +void RServiceSession::ipcFailure(QService::UnrecoverableIPCError err) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("RServiceSession::ipcFailure ipc Fault reported"); +#endif + emit errorUnrecoverableIPCFault(err); +} + +static const TUint myRangeCount = 1; +static const TInt myRanges[myRangeCount] = + { + 0 //range is 0-Max inclusive + }; +static const TUint8 myElementsIndex[myRangeCount] = + { + CPolicyServer::EAlwaysPass + }; +static const CPolicyServer::TPolicyElement myElements[] = + { + {_INIT_SECURITY_POLICY_PASS } // Dummy entry + }; +static const CPolicyServer::TPolicy myPolicy = + { + CPolicyServer::ECustomCheck, //specifies all connect attempts should pass + myRangeCount, + myRanges, + myElementsIndex, + myElements, + }; + +CServiceProviderServer::CServiceProviderServer(QRemoteServiceRegisterSymbianPrivate* aOwner) + : CPolicyServer(EPriorityNormal, myPolicy), iSessionCount(0), iOwner(aOwner), iFilter(0) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("CServiceProviderServer constructor"); +#endif + Q_ASSERT(aOwner); +} + +CPolicyServer::TCustomResult CServiceProviderServer::CustomSecurityCheckL(const RMessage2 &aMessage, TInt &aAction, TSecurityInfo &aMissing) +{ + if(iFilter){ + if(iFilter(reinterpret_cast<const void *>(&aMessage))){ + return CPolicyServer::EPass; + } + else { + return CPolicyServer::EFail; + } + } + return CPolicyServer::EPass; +} + +CSession2* CServiceProviderServer::NewSessionL(const TVersion &aVersion, const RMessage2 &aMessage) const +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("CServiceProviderServer::NewSessionL()"); +#endif + if (!User::QueryVersionSupported(TVersion(KServerMajorVersionNumber, + KServerMinorVersionNumber, KServerBuildVersionNumber), aVersion)) + { + User::Leave(KErrNotSupported); + } + CServiceProviderServerSession* session = CServiceProviderServerSession::NewL(*const_cast<CServiceProviderServer*>(this)); + iOwner->processIncoming(session); + return session; +} + +void CServiceProviderServer::IncreaseSessions() +{ + iSessionCount++; +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << ">>>> CServiceProviderServer incremented session count to: " << iSessionCount; +#endif +} + +void CServiceProviderServer::DecreaseSessions() +{ + iSessionCount--; +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "<<<< CServiceProviderServer decremented session count to: " << iSessionCount; +#endif + if(iSessionCount == 0){ + Cancel(); + if(iOwner->quitOnLastInstanceClosed()) + QCoreApplication::exit(); + } +} + +void CServiceProviderServer::setSecurityFilter(QRemoteServiceRegister::SecurityFilter filter) +{ + iFilter = filter; +} + +CServiceProviderServerSession *CServiceProviderServerSession::NewL(CServiceProviderServer &aServer) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("CServiceProviderServerSession::NewL()"); +#endif + CServiceProviderServerSession *self = CServiceProviderServerSession::NewLC(aServer); + CleanupStack::Pop(self); + return self; +} + +CServiceProviderServerSession *CServiceProviderServerSession::NewLC(CServiceProviderServer &aServer) +{ + CServiceProviderServerSession* self = new (ELeave) CServiceProviderServerSession(aServer); + CleanupStack::PushL(self); + self->ConstructL(); + return self; +} + +CServiceProviderServerSession::CServiceProviderServerSession(CServiceProviderServer &aServer) + : iServer(aServer) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("CServiceProviderServerSession constructor"); +#endif + iServer.IncreaseSessions(); +} + +void CServiceProviderServerSession::ConstructL() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("OTR CServiceProviderServerSession::ConstructL()"); +#endif + iTotalSize = 0; // No data + iBlockData.clear();// clear the buffer +} + +CServiceProviderServerSession::~CServiceProviderServerSession() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("CServiceProviderServerSession destructor"); +#endif + iServer.DecreaseSessions(); + delete iByteArray; + delete iOwner; +} + +void CServiceProviderServerSession::ServiceL(const RMessage2 &aMessage) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "CServiceProviderServerSession::ServiceL for message: " << aMessage.Function(); +#endif + switch (aMessage.Function()) + { + case EServicePackage: + HandleServicePackageL(aMessage); + aMessage.Complete(KErrNone); + break; + case EPackageRequest: + HandlePackageRequestL(aMessage); + break; + case EPackageRequestCancel: + HandlePackageRequestCancelL(aMessage); + break; + } +} + +void CServiceProviderServerSession::HandleServicePackageL(const RMessage2& aMessage) +{ + // Reproduce the serialized data. + HBufC8* servicePackageBuf8 = HBufC8::New(aMessage.GetDesLength(0)); + if (!servicePackageBuf8) { + User::Leave( KErrNoMemory ); + } + + TPtr8 ptrToBuf(servicePackageBuf8->Des()); + TInt ret = KErrNone; + TRAP(ret, aMessage.ReadL(0, ptrToBuf)); + if (ret != KErrNone) { + // TODO: is this error handleing correct +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "HandleServicePackageL. Message read failed: " << ret; +#endif + //iDb->lastError().setError(DBError::UnknownError); + //aMessage.Write(1, LastErrorCode()); + delete servicePackageBuf8; + User::Leave( ret ); + } + + QByteArray byteArray((const char*)ptrToBuf.Ptr(), ptrToBuf.Length()); + QDataStream in(byteArray); + QServicePackage results; + in >> results; + +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "CServiceProviderServerSession Reproduced service package: "; + printServicePackage(results); +#endif + iOwner->packageReceived(results); + delete servicePackageBuf8; +} + +void CServiceProviderServerSession::SetParent(SymbianServerEndPoint *aOwner) +{ + iOwner = aOwner; +} + +void CServiceProviderServerSession::HandlePackageRequestL(const RMessage2& aMessage) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("HandlePackageRequestL(). Setting pending true and storing message."); +#endif + iMsg = aMessage; + iPendingPackageRequest = ETrue; + if(!iPendingPackageQueue.isEmpty()) + SendServicePackageL(iPendingPackageQueue.dequeue()); +} + +void CServiceProviderServerSession::HandlePackageRequestCancelL(const RMessage2& aMessage) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("HandlePackageRequestCancelL"); +#endif + if (iPendingPackageRequest) { + iMsg.Complete(KErrCancel); + iPendingPackageRequest = EFalse; + } + aMessage.Complete(EPackageRequestComplete); +} + +void CServiceProviderServerSession::SendServicePackageL(const QServicePackage& aPackage) +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("CServiceProviderServerSession:: SendServicePackage for package: "); + printServicePackage(aPackage); +#endif + if (iPendingPackageRequest) { + if(iBlockData.isEmpty()){ + // Serialize the package + QDataStream out(&iBlockData, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_6); + out << aPackage; + iTotalSize = iBlockData.size(); + } +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "Size of package sent from server to client: " << iBlockData.count(); + qDebug() << "Size of buffer from client: " << iMsg.GetDesMaxLength(0); +#endif + + int size = iBlockData.size(); + if(size > iMsg.GetDesMaxLength(0)){ + size = iMsg.GetDesMaxLength(0); + // enequeue the package so we send the next chunk + // when the next request comes through + iPendingPackageQueue.prepend(aPackage); + } + TPtrC8 ptr8((TUint8*)(iBlockData.constData()), size); + iMsg.WriteL(0, ptr8); + iBlockData.remove(0, size); +//#ifdef QT_SFW_SYMBIAN_IPC_DEBUG +// if(status == KErrOverflow){ +// qDebug() << "OTR Server to client, got overflow, sending 0 bytes"; +// } +// else if(status != KErrNone){ +// qDebug() << "OTR SendServicePackage: error code from send: " << status; +// } +//#endif + TPckgBuf<TInt> totalSize(iTotalSize); + iMsg.WriteL(1,totalSize); + iMsg.Complete(EPackageRequestComplete); + iPendingPackageRequest = EFalse; + } else { + iPendingPackageQueue.enqueue(aPackage); +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qWarning() << "RTR SendServicePackage: service package from server to client queued - no pending receive request."; +#endif + } +} + +ServiceMessageListener::ServiceMessageListener(RServiceSession* aSession, SymbianClientEndPoint* aOwner) + : CActive(EPriorityNormal), + iClientSession(aSession), + iOwnerEndPoint(aOwner) +{ + Q_ASSERT(iClientSession); + Q_ASSERT(iOwnerEndPoint); +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("ServiceMessageListener constructor"); +#endif + aSession->setListener(this); + CActiveScheduler::Add(this); + StartListening(); +} + +ServiceMessageListener::~ServiceMessageListener() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("ServiceMessageListener destructor"); +#endif + Cancel(); +} + +void ServiceMessageListener::StartListening() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("ServiceMessageListener::StartListening"); +#endif + iClientSession->ListenForPackages(iStatus); + SetActive(); +} + +void ServiceMessageListener::DoCancel() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug("ServiceMessageListener::DoCancel"); +#endif + iClientSession->CancelListenForPackages(); +} + +void ServiceMessageListener::RunL() +{ +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "ServiceMessageListener::RunL for iStatus.Int(should be 4): " << iStatus.Int(); +#endif + if (iStatus.Int() == EPackageRequestComplete) { + // Client side has received a service package from server. Pass it onwards and + // issue new pending request. +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "RunL length of the package received client side is: " << iClientSession->iMessageFromServer.Length(); +#endif + if(iClientSession->iMessageFromServer.Length() == 0){ + // we received 0 bytes from the other side, + // normally because it tried to write more bytes + // than were in the TDes + User::Leave(KErrTooBig); + } + + if(!iByteArray.length()){ + /* Helps client session to calculate an optimum IPC buffer size */ + iClientSession->addDataSize(iClientSession->iSize()); + } + + iByteArray.append((const char*)iClientSession->iMessageFromServer.Ptr(), + iClientSession->iMessageFromServer.Length()); + if(iByteArray.length() >= iClientSession->iSize()){ + QDataStream in(iByteArray); + iByteArray.clear(); + QServicePackage results; + in >> results; +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG + qDebug() << "ServiceMessageListener Reproduced service package: "; + printServicePackage(results); +#endif + iOwnerEndPoint->PackageReceived(results); + } + StartListening(); + } + else if(iStatus.Int() < 0){ + TInt s = iStatus.Int(); + switch(s){ + case KErrServerTerminated: + iClientSession->ipcFailure(QService::ErrorServiceNoLongerAvailable); + break; + case KErrServerBusy: + case KErrNoMemory: + iClientSession->ipcFailure(QService::ErrorOutofMemory); + break; + } + } +} + +#include "moc_qremoteserviceregister_s60_p.cpp" +#include "qremoteserviceregister_s60.moc" +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/qremoteserviceregister_s60_p.h b/src/serviceframework/ipc/qremoteserviceregister_s60_p.h new file mode 100644 index 00000000..48799546 --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_s60_p.h @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTESERVICEREGISTER_S60_P_H +#define QREMOTESERVICEREGISTER_S60_P_H + +//#define QT_SFW_SYMBIAN_IPC_DEBUG + +#include "qremoteserviceregister.h" +#include "qremoteserviceregister_p.h" +//#include "qremoteserviceclassregister.h" +#include "qservicepackage_p.h" +#include "qservice.h" +#include <e32base.h> + +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG +#include <QDebug> +#endif + +#include <QQueue> + +QTM_BEGIN_NAMESPACE + +class ServiceMessageListener; + +const TUint KServerMajorVersionNumber = 1; +const TUint KServerMinorVersionNumber = 0; +const TUint KServerBuildVersionNumber = 0; + +enum TServiceProviderRequest +{ + EServicePackage = 1, + EPackageRequest = 2, + EPackageRequestCancel = 3, + EPackageRequestComplete = 4 +}; + +const TInt KIpcBufferMinimumSize = 256; //Bytes +const TInt KIpcBufferMaximumSize = 1024*16; + +// Forward declarations +class ObjectEndPoint; +class CServiceProviderServerSession; +class CServiceProviderServer; +class SymbianServerEndPoint; +class SymbianClientEndPoint; +class QRemoteServiceRegisterSymbianPrivate; + +// Type definitions +typedef TPckgBuf<TInt> TError; + +#ifdef QT_SFW_SYMBIAN_IPC_DEBUG +void printServicePackage(const QServicePackage& package); +#endif + +//Internal class which calculates the Simple and Weighted moving average of +//data sizes from the Service. The average calculated can be used to decide a +//optimal IPC buffer size. +template <int WINDOW> +class MovingAverage +{ +public: + MovingAverage(TInt median):m_index(0),m_averageSimple((TReal)median),m_averageWeighted((TReal)median) + { + m_weightedSum = m_sum =0; + for (TInt i=0;i<WINDOW;i++){ + m_samples[i]=median; + m_weightedSum += m_samples[i]*(i+1); + m_sum += m_samples[i]; + } + } + int average(){return static_cast<TInt>(m_averageSimple);} + int averageWeighted(){return static_cast<TInt>(m_averageWeighted);} + void addSample(int sample) + { + int pop = m_samples[m_index]; + m_samples[m_index] = sample; + m_weightedSum = m_weightedSum + WINDOW*sample - m_sum; + m_sum = m_sum + sample - pop; + m_averageSimple = m_averageSimple + ((TReal)sample -pop)/WINDOW; + m_averageWeighted = (TReal)m_weightedSum*2/(WINDOW*(WINDOW+1)); + m_index = (m_index+1)%WINDOW; + } +private: + int m_samples[WINDOW]; + TInt m_index; + TInt m_weightedSum; + /* The sum of all the samples */ + TInt m_sum; + TReal m_averageSimple; + TReal m_averageWeighted; +}; + + +// Internal class handling the actual communication with the service provider. +// Communication is based on standard Symbian client-server architecture. +class RServiceSession : public QObject, public RSessionBase +{ + Q_OBJECT +public: + RServiceSession(QString address); + virtual ~RServiceSession(); + TInt Connect(); + void Close(); + TVersion Version() const; + void SendServicePackage(const QServicePackage& aPackage); + /* Adds sample to calculate the average */ + void addDataSize(TInt dataSize); + + public: + RBuf8 iMessageFromServer; + TPckgBuf<TInt> iSize; // TPckgBuf type can be used directly as IPC parameter + + void setListener(ServiceMessageListener* listener); + +public slots: + void ipcFailure(QService::UnrecoverableIPCError); + +signals: + void Disconnected(); + void errorUnrecoverableIPCFault(QService::UnrecoverableIPCError); + +protected: + void ListenForPackages(TRequestStatus& aStatus); + void CancelListenForPackages(); + +private: + TInt StartServer(); + /* Re-Sizes IPC buffer if needed */ + void updateIpcBufferSize(); + +private: + TIpcArgs iArgs; // These two are used in actively listening to server + TError iError; + QString iServerAddress; + ServiceMessageListener* iListener; + MovingAverage<10> iDataSizes; + TBool iServerStarted; + friend class ServiceMessageListener; +}; + +// needed for creating server thread. +const TUint KDefaultHeapSize = 0x10000; + +class CServiceProviderServer : public CPolicyServer + { + public: + CServiceProviderServer(QRemoteServiceRegisterSymbianPrivate* aOwner); + CSession2* NewSessionL(const TVersion& aVersion, const RMessage2& aMessage) const; + + public: + + void IncreaseSessions(); + void DecreaseSessions(); + + void setSecurityFilter(QRemoteServiceRegister::SecurityFilter filter); + + protected: + virtual TCustomResult CustomSecurityCheckL(const RMessage2 &,TInt &,TSecurityInfo &); + + private: + + int iSessionCount; + QRemoteServiceRegisterSymbianPrivate* iOwner; + QRemoteServiceRegister::SecurityFilter iFilter; + }; + +class CServiceProviderServerSession : public CSession2 + { + public: + static CServiceProviderServerSession* NewL(CServiceProviderServer& aServer); + static CServiceProviderServerSession* NewLC(CServiceProviderServer& aServer); + virtual ~CServiceProviderServerSession(); + void ServiceL(const RMessage2& aMessage); + void SetParent(SymbianServerEndPoint* aOwner); + void SendServicePackageL(const QServicePackage& aPackage); + + void HandleServicePackageL(const RMessage2& aMessage); + void HandlePackageRequestL(const RMessage2& aMessage); + void HandlePackageRequestCancelL(const RMessage2& aMessage); + + private: + CServiceProviderServerSession(CServiceProviderServer& aServer); + void ConstructL(); + + private: + CServiceProviderServer& iServer; + SymbianServerEndPoint* iOwner; + QByteArray* iByteArray; + RMessage2 iMsg; // For replying pending service package requests + QQueue<QServicePackage> iPendingPackageQueue; + TBool iPendingPackageRequest; + QByteArray iBlockData; + int iTotalSize; + }; + + +class QRemoteServiceRegisterSymbianPrivate: public QRemoteServiceRegisterPrivate +{ + Q_OBJECT + +public: + QRemoteServiceRegisterSymbianPrivate(QObject* parent); + ~QRemoteServiceRegisterSymbianPrivate(); + void publishServices(const QString& ident ); + static QObject* proxyForService(const QRemoteServiceRegister::Entry& entry, const QString& location); + void processIncoming(CServiceProviderServerSession* session); + + virtual QRemoteServiceRegister::SecurityFilter setSecurityFilter(QRemoteServiceRegister::SecurityFilter filter); + +private: + CServiceProviderServer *m_server; +}; + +// A helper class that actively listens for serviceprovider messages. +// Needed because Symbian server cannot send messages without active request +// from the client. +class ServiceMessageListener : public CActive +{ +public: + ServiceMessageListener(RServiceSession* aSession, SymbianClientEndPoint* aOwner); + ~ServiceMessageListener(); + +protected: + void StartListening(); + // from CActive baseclass + void DoCancel(); + void RunL(); + +private: + RServiceSession* iClientSession; + SymbianClientEndPoint* iOwnerEndPoint; + QByteArray iByteArray; +}; + +QTM_END_NAMESPACE + +#endif // QREMOTESERVICEREGISTER_S60_P_H diff --git a/src/serviceframework/ipc/qremoteserviceregisterentry_p.h b/src/serviceframework/ipc/qremoteserviceregisterentry_p.h new file mode 100644 index 00000000..8aa0cfa9 --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregisterentry_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTESERVICEREGISTERENTRY_P_H +#define QREMOTESERVICEREGISTERENTRY_P_H + +#include <QExplicitlySharedDataPointer> +#include <QString> + +#include "qremoteserviceregister.h" + +QTM_BEGIN_NAMESPACE + +class QRemoteServiceRegisterEntryPrivate : public QSharedData +{ +public: + QRemoteServiceRegisterEntryPrivate() + : meta(0), cptr(0), instanceType(QRemoteServiceRegister::PrivateInstance) + { + } + + QRemoteServiceRegisterEntryPrivate(QRemoteServiceRegisterEntryPrivate &other) + : QSharedData(other), iface(other.iface), + service(other.service), ifaceVersion(other.ifaceVersion), + meta(other.meta), cptr(other.cptr), instanceType(other.instanceType) + { + } + + QString iface; + QString service; + QString ifaceVersion; + const QMetaObject* meta; + QRemoteServiceRegister::CreateServiceFunc cptr; + QRemoteServiceRegister::InstanceType instanceType; +}; + +QTM_END_NAMESPACE + +#endif diff --git a/src/serviceframework/ipc/qservicemetaobject_dbus.cpp b/src/serviceframework/ipc/qservicemetaobject_dbus.cpp new file mode 100644 index 00000000..6dbb79c1 --- /dev/null +++ b/src/serviceframework/ipc/qservicemetaobject_dbus.cpp @@ -0,0 +1,580 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qservicemetaobject_dbus_p.h" +#include "qmetaobjectbuilder_p.h" +#include "qsignalintercepter_p.h" +#include <QDebug> + +QTM_BEGIN_NAMESPACE + +class ServiceMetaSignalIntercepter : public QSignalIntercepter +{ + +public: + ServiceMetaSignalIntercepter(QObject* sender, const QByteArray& signal, QServiceMetaObjectDBus* parent) + : QSignalIntercepter(sender, signal, parent), serviceDBus(parent) + { + + } + + void setMetaIndex(int index) + { + metaIndex = index; + } + +protected: + void activated( const QList<QVariant>& args ) + { + serviceDBus->activateMetaSignal(metaIndex, args); + } +private: + QServiceMetaObjectDBus* serviceDBus; + int metaIndex; +}; + + +class QServiceMetaObjectDBusPrivate +{ +public: + QObject *service; + const QMetaObject *serviceMeta; + const QMetaObject *dbusMeta; +}; + +QServiceMetaObjectDBus::QServiceMetaObjectDBus(QObject* service, bool signalsObject) + : QDBusAbstractAdaptor(service) +{ + // Register our DBus custom type object + qRegisterMetaType<QTM_PREPEND_NAMESPACE(QServiceUserTypeDBus)>(); + qDBusRegisterMetaType<QTM_PREPEND_NAMESPACE(QServiceUserTypeDBus)>(); + + // Generate our DBus meta object + d = new QServiceMetaObjectDBusPrivate(); + d->service = service; + d->serviceMeta = service->metaObject(); + d->dbusMeta = dbusMetaObject(signalsObject); + + // Relay signals from the service to the constructed DBus meta object + connectMetaSignals(signalsObject); +} + +QServiceMetaObjectDBus::~QServiceMetaObjectDBus() +{ + if (d->dbusMeta) + qFree(const_cast<QMetaObject*>(d->dbusMeta)); + + delete d; +} + +/*! + Relays all signals from the service object to the converted DBus service adaptor so + that when a service signal is emitted the DBus object will emit the counterpart signal +*/ +void QServiceMetaObjectDBus::connectMetaSignals(bool signalsObject) { + if (!signalsObject) { + // Automatically relay signals from service object to adaptor + setAutoRelaySignals(true); + + // Connect signals with custom arguments + int methodCount = d->serviceMeta->methodCount(); + for (int i = 0; i < methodCount; i++) { + QMetaMethod mm = d->serviceMeta->method(i); + + + if (mm.methodType() == QMetaMethod::Signal) { + QByteArray sig(mm.signature()); + bool customType = false; + const QList<QByteArray> pTypes = mm.parameterTypes(); + const int pTypesCount = pTypes.count(); + + // Ignore all QObject calls + const QMetaObject *mo = QObject::metaObject(); + int qobjectIndex = mo->indexOfMethod(sig); + if (qobjectIndex >= 0) + continue; + + // Detects custom types as passed arguments + for (int arg = 0; arg < pTypesCount; arg++) { + const QByteArray& type = pTypes[arg]; + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + sig.replace(QByteArray(type), QByteArray("QDBusVariant")); + customType = true; + } + } + + // Connects the service signal to the corresponding DBus service signal + if (customType) { + QByteArray signal = mm.signature(); + ServiceMetaSignalIntercepter *intercept = + new ServiceMetaSignalIntercepter(d->service, "2"+signal, this); + intercept->setMetaIndex(i); + } + } + } + } +} + +/*! + Relays the activation to the DBus object signal with the given id and arguments list when the + intercepter for signals with custom arguments has been activated. This bypasses the metacall + which usually does the relaying for signals with standard arguments since no pre-connection + conversions are required. +*/ +void QServiceMetaObjectDBus::activateMetaSignal(int id, const QVariantList& args) +{ + QMetaMethod method = d->serviceMeta->method(id); + + // Converts the argument list to values supported by the QtDBus type system + QVariantList convertedList = args; + QByteArray sig(method.signature()); + QList<QByteArray> params = method.parameterTypes(); + + for (int i = 0; i < params.size(); i++) { + QVariant dbusVariant = args[i]; + + // Convert custom types + const QByteArray& type = params[i]; + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + variantType = QMetaType::type(type); + + if (variantType >= QMetaType::User) { + // Wrap custom types in a QDBusVariant of the type name and + // a buffer of its variant-wrapped data + QByteArray buffer; + QDataStream stream(&buffer, QIODevice::ReadWrite | QIODevice::Append); + stream << args[i]; + + QServiceUserTypeDBus customType; + customType.typeName = type; + customType.variantBuffer = buffer; + + QDBusVariant replacement(QVariant::fromValue(customType)); + convertedList.replace(i, QVariant::fromValue(replacement)); + } + + sig.replace(QByteArray(type), QByteArray("QDBusVariant")); + } + } + + // Activate the DBus object signal call + const int numArgs = convertedList.size(); + QVarLengthArray<void *, 32> a( numArgs+1 ); + a[0] = 0; + + const QList<QByteArray> pTypes = method.parameterTypes(); + for ( int arg = 0; arg < numArgs; ++arg ) { + if (pTypes.at(arg) == "QVariant") + a[arg+1] = (void *)&( convertedList[arg] ); + else + a[arg+1] = (void *)( convertedList[arg].data() ); + } + + int dbusIndex = d->dbusMeta->indexOfSignal(sig); + QMetaObject::activate(this, dbusIndex, a.data()); +} + + +/*! + Build a metaobject that represents the service object as a valid service that + satisfies the QtDBus type system. +*/ +const QMetaObject* QServiceMetaObjectDBus::dbusMetaObject(bool signalsObject) const +{ + // Create a meta-object to represent all the contents of our service on DBus + QMetaObjectBuilder builder; + + builder.setClassName(d->serviceMeta->className()); + builder.setSuperClass(d->serviceMeta->superClass()); // needed? + + const QMetaObject* mo = d->serviceMeta; + while (mo && strcmp(mo->className(), "QObject")) { + // Add our methods, signals and slots + for (int i = mo->methodOffset(); i < mo->methodCount(); i++) { + QMetaMethod mm = mo->method(i); + + if (signalsObject && mm.methodType() != QMetaMethod::Signal) + continue; + + // Convert QVariant and custom return types to QDBusVariants + QByteArray ret(mm.typeName()); + const QByteArray& type = mm.typeName(); + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + ret = QByteArray("QDBusVariant"); + } + + // Convert QVariant and custom argument types to QDBusVariants + QByteArray sig(mm.signature()); + const QList<QByteArray> pTypes = mm.parameterTypes(); + const int pTypesCount = pTypes.count(); + for (int i=0; i < pTypesCount; i++) { + const QByteArray& type = pTypes[i]; + int variantType = QVariant::nameToType(type); + if (variantType == QVariant::UserType) { + sig.replace(QByteArray(type), QByteArray("QDBusVariant")); + } + } + + // Add a MetaMethod with converted signature to our builder + QMetaMethodBuilder method; + switch (mm.methodType()) { + case QMetaMethod::Method: + method = builder.addMethod(sig); + break; + case QMetaMethod::Slot: + method = builder.addSlot(sig); + break; + case QMetaMethod::Signal: + method = builder.addSignal(sig); + break; + default: + break; + } + + // Make sure our built MetaMethod is identical, excluding conversion + method.setReturnType(ret); + method.setParameterNames(mm.parameterNames()); + method.setTag(mm.tag()); + method.setAccess(mm.access()); + method.setAttributes(mm.attributes()); + } + + if (signalsObject) + return builder.toMetaObject(); + + // Add our property accessor methods + // NOTE: required because read/reset properties over DBus require adaptors + // otherwise a metacall won't be invoked as QMetaObject::ReadProperty + // or QMetaObject::ResetProperty + QMetaMethodBuilder readProp; + readProp = builder.addMethod(QByteArray("propertyRead(QString)")); + readProp.setReturnType(QByteArray("QDBusVariant")); + QList<QByteArray> params; + params << QByteArray("name"); + readProp.setParameterNames(params); + + QMetaMethodBuilder resetProp; + resetProp = builder.addMethod(QByteArray("propertyReset(QString)")); + QList<QByteArray> paramsReset; + paramsReset << QByteArray("name"); + resetProp.setParameterNames(paramsReset); + + + // Add our properties/enums + int propCount = d->serviceMeta->propertyCount(); + for (int i=0; i<propCount; i++) { + QMetaProperty mp = d->serviceMeta->property(i); + + QMetaPropertyBuilder property = builder.addProperty(mp.name(), mp.typeName()); + property.setReadable(mp.isReadable()); + property.setWritable(mp.isWritable()); + property.setResettable(mp.isResettable()); + property.setDesignable(mp.isDesignable()); + property.setScriptable(mp.isScriptable()); + property.setStored(mp.isStored()); + property.setEditable(mp.isEditable()); + property.setUser(mp.isUser()); + property.setStdCppSet(mp.hasStdCppSet()); + property.setEnumOrFlag(mp.isEnumType()); + } + + // Needs Enumerators support when QtDBus supports + + mo = mo->superClass(); + } + + // return our constructed dbus metaobject + return builder.toMetaObject(); +} + +/*! + Provide custom Q_OBJECT implementation of the metaObject +*/ +const QMetaObject* QServiceMetaObjectDBus::metaObject() const +{ + // Provide our construected DBus service metaobject + return d->dbusMeta; +} + +/*! + Overrides metacall which relays the DBus service call to the actual service + meta object. Positive return will indicate that the metacall was unsuccessful +*/ +int QServiceMetaObjectDBus::qt_metacall(QMetaObject::Call c, int id, void **a) +{ + int dbusIndex = id; + + // Relay the meta-object call to the service object + if (c == QMetaObject::InvokeMetaMethod) { + // METHOD CALL + QMetaMethod method = d->dbusMeta->method(id); + + const bool isSignal = (method.methodType() == QMetaMethod::Signal); + + ///////////////////// CHECK SPECIAL PROPERTY /////////////////////// + // This is required because property READ/RESET doesn't function + // as desired over DBus without the use of adaptors. Temporary + // methods propertyRead and propertyReset are added to the published + // meta object and relay the correct property call + QString methodName(QLatin1String(method.signature())); + methodName.truncate(methodName.indexOf(QLatin1String("("))); + + if (methodName == QLatin1String("propertyRead")) { + QString propertyName = *reinterpret_cast<QString*>(a[1]); + int index = d->dbusMeta->indexOfProperty(propertyName.toLatin1().constData()); + id = qt_metacall(QMetaObject::ReadProperty, index, a); + return id; + + } else if (methodName == QLatin1String("propertyReset")) { + QString propertyName = *reinterpret_cast<QString*>(a[1]); + int index = d->dbusMeta->indexOfProperty(propertyName.toLatin1().constData()); + id = qt_metacall(QMetaObject::ResetProperty, index, a); + return id; + } + //////////////////////////////////////////////////////////////////// + + // Find the corresponding signature to our service object + QByteArray sig(method.signature()); + int count = methodName.size() + 1; + const QList<QByteArray> xTypes = method.parameterTypes(); + const int xTypesCount = xTypes.count(); + for (int i=0; i < xTypesCount; i++) { + const QByteArray& t = xTypes[i]; + int variantType = QVariant::nameToType(t); + if (variantType == QVariant::UserType) { + variantType = QMetaType::type(t); + } + + // Check for QVariants or custom types, represented as QDBusVariants + if (t == "QDBusVariant") { + // Convert QDBusVariant to QVariant + QVariant convert = QVariant(variantType, a[i+1]); + QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(convert); + QVariant variant = dbusVariant.variant(); + + // Is a custom argument if castable to QDBusArgument + bool hasCustomType = variant.canConvert<QDBusArgument>(); + QByteArray replacement("QVariant"); + + // Custom types will have QDBusArgument streaming operators for + // the QServiceUserTypeDBus object. Extract the real type name + // and buffered QVariant from this + if (hasCustomType) { + QDBusArgument demarshall = variant.value<QDBusArgument>(); + QServiceUserTypeDBus userType = qdbus_cast<QServiceUserTypeDBus>(demarshall); + *reinterpret_cast<QVariant*>(a[i+1]) = userType.variantBuffer; + + replacement = userType.typeName; + } + + // Replace "QDBusVariant" with "QVariant" or custom type name + sig.replace(count, 12, replacement); + count += replacement.size(); + + } else { + // Supported type so skip this paramater + count += t.size(); + } + + // Skips the comma if not last parameter + if (i < xTypesCount) + count += 1; + } + + // Find the corresponding method metaindex to our service object + id = d->serviceMeta->indexOfMethod(sig); + QMetaMethod mm = d->serviceMeta->method(id); + + const QList<QByteArray> pTypes = mm.parameterTypes(); + const int pTypesCount = pTypes.count(); + + const char* typeNames[] = {0,0,0,0,0,0,0,0,0,0}; + const void* params[] = {0,0,0,0,0,0,0,0,0,0}; + bool hasCustomType = false; + + // Process arguments + for (int i=0; i < pTypesCount; i++) { + const QByteArray& t = pTypes[i]; + int variantType = QVariant::nameToType(t); + if (variantType == QVariant::UserType) { + variantType = QMetaType::type(t); + } + + if (variantType >= QMetaType::User) { + // Custom argument + QVariant convert = QVariant(QVariant::ByteArray, a[i+1]); + QByteArray buffer = convert.toByteArray(); + QDataStream stream(&buffer, QIODevice::ReadWrite); + + // Load our buffered variant-wrapped custom type + QVariant *customType = new QVariant(variantType, (const void*)0); + QMetaType::load(stream, QMetaType::type("QVariant"), customType); + + typeNames[i] = customType->typeName(); + params[i] = customType->constData(); + hasCustomType = true; + } + } + + // Check if this is a signal emit and activate + if (isSignal) { + QMetaObject::activate(this, dbusIndex, a); + return id; + } + + // Check for custom return types and make the metacall + const QByteArray& type = mm.typeName(); + int retType = QVariant::nameToType(type); + retType = QMetaType::type(type); + if (retType >= QMetaType::User) { + // Invoke the object method directly for custom return types + bool result = false; + QVariant returnValue = QVariant(retType, (const void*)0); + QGenericReturnArgument ret(type, returnValue.data()); + result = mm.invoke(d->service, ret, + QGenericArgument(typeNames[0], params[0]), + QGenericArgument(typeNames[1], params[1]), + QGenericArgument(typeNames[2], params[2]), + QGenericArgument(typeNames[3], params[3]), + QGenericArgument(typeNames[4], params[4]), + QGenericArgument(typeNames[5], params[5]), + QGenericArgument(typeNames[6], params[6]), + QGenericArgument(typeNames[7], params[7]), + QGenericArgument(typeNames[8], params[8]), + QGenericArgument(typeNames[9], params[9])); + + if (result) { + // Wrap custom return type in a QDBusVariant of the type + // and a buffer of its variant-wrapped data + QByteArray buffer; + QDataStream stream(&buffer, QIODevice::WriteOnly | QIODevice::Append); + stream << returnValue; + + QServiceUserTypeDBus customType; + customType.typeName = type; + customType.variantBuffer = buffer; + + QDBusVariant replacement(QVariant::fromValue(customType)); + *reinterpret_cast<QDBusVariant*>(a[0]) = replacement; + + // Return negative id to say metacall was handled externally + return -1; + } + + } else { + // Void or standard return types + if (hasCustomType == true) { + // Invoke the object method directly for custom arguments + bool result = false; + result = mm.invoke(d->service, + QGenericArgument(typeNames[0], params[0]), + QGenericArgument(typeNames[1], params[1]), + QGenericArgument(typeNames[2], params[2]), + QGenericArgument(typeNames[3], params[3]), + QGenericArgument(typeNames[4], params[4]), + QGenericArgument(typeNames[5], params[5]), + QGenericArgument(typeNames[6], params[6]), + QGenericArgument(typeNames[7], params[7]), + QGenericArgument(typeNames[8], params[8]), + QGenericArgument(typeNames[9], params[9])); + if (result) { + // Return negative id to say metacall was handled externally + return -1; + } + } else { + // Relay standard metacall to service object + id = d->service->qt_metacall(c, id, a); + } + } + + } else { + // PROPERTY CALL + + // Find the corresponding property metaindex of our service object + QMetaProperty property = d->dbusMeta->property(id); + QByteArray name(property.name()); + id = d->serviceMeta->indexOfProperty(name); + + if (c == QMetaObject::ReadProperty) { + // Convert to DBusVariant + QMetaProperty mp = d->serviceMeta->property(id); + QVariant value = mp.read(d->service); + QDBusVariant replacement(value); + *reinterpret_cast<QDBusVariant*>(a[0]) = replacement; + + // Return negative id to say metacall was handled externally + return -1; + } + + // Metacall our service object property + id = d->service->qt_metacall(c, id, a); + } + + return id; +} + +void *QServiceMetaObjectDBus::qt_metacast(const char* className) +{ + if (!className) return 0; + //this object should not be castable to anything but QObject + return QObject::qt_metacast(className); +} + +QDBusArgument &operator<<(QDBusArgument &argument, const QServiceUserTypeDBus &myType) +{ + argument.beginStructure(); + argument << myType.typeName << myType.variantBuffer; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QServiceUserTypeDBus &myType) +{ + argument.beginStructure(); + argument >> myType.typeName >> myType.variantBuffer; + argument.endStructure(); + return argument; +} + +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/qservicemetaobject_dbus_p.h b/src/serviceframework/ipc/qservicemetaobject_dbus_p.h new file mode 100644 index 00000000..17490456 --- /dev/null +++ b/src/serviceframework/ipc/qservicemetaobject_dbus_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICE_METAOBJECT_DBUS_H +#define QSERVICE_METAOBJECT_DBUS_H + +#include "qserviceframeworkglobal.h" +#include "objectendpoint_dbus_p.h" +#include <QObject> + +QTM_BEGIN_NAMESPACE + +class QServiceMetaObjectDBusPrivate; +class QServiceMetaObjectDBus : public QDBusAbstractAdaptor +{ + +public: + QServiceMetaObjectDBus(QObject* service, bool signalsObject=false); + virtual ~QServiceMetaObjectDBus(); + + virtual const QMetaObject* metaObject() const; + int qt_metacall(QMetaObject::Call c, int id, void **a); + void *qt_metacast(const char* className); + + void activateMetaSignal(int id, const QVariantList& args); + +protected: + //void connectNotify(const char* signal); + //void disconnectNotify(const char* signal); + +private: + const QMetaObject* dbusMetaObject(bool signalsObject) const; + void connectMetaSignals(bool signalsObject); + + QServiceMetaObjectDBusPrivate* d; + QVector<bool> localSignals; +}; + + +struct QServiceUserTypeDBus +{ + QByteArray typeName; + QByteArray variantBuffer; +}; + +QDBusArgument &operator<<(QDBusArgument &argument, const QServiceUserTypeDBus &myType); +const QDBusArgument &operator>>(const QDBusArgument &argument, QServiceUserTypeDBus &myType); + +QTM_END_NAMESPACE + +Q_DECLARE_METATYPE(QTM_PREPEND_NAMESPACE(QServiceUserTypeDBus)) + +#endif //QSERVICE_METAOBJECT_DBUS_H diff --git a/src/serviceframework/ipc/qservicepackage.cpp b/src/serviceframework/ipc/qservicepackage.cpp new file mode 100644 index 00000000..6a9429a4 --- /dev/null +++ b/src/serviceframework/ipc/qservicepackage.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qservicepackage_p.h" +#include <QDebug> + +QTM_BEGIN_NAMESPACE + +QServicePackage::QServicePackage() + : d(0) +{ + +} + +QServicePackage::QServicePackage(const QServicePackage& other) + : d(other.d) +{ +} + +QServicePackage& QServicePackage::operator=(const QServicePackage& other) +{ + d = other.d; + return *this; +} + +QServicePackage::~QServicePackage() +{ +} + +bool QServicePackage::isValid() const +{ + return d; +} + +QServicePackage QServicePackage::createResponse() const +{ + Q_ASSERT(d->responseType == QServicePackage::NotAResponse); + QServicePackage response; + response.d = new QServicePackagePrivate(); + response.d->packageType = d->packageType; + response.d->messageId = d->messageId; + response.d->instanceId = d->instanceId; + response.d->responseType = QServicePackage::Failed; + + return response; +} + +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &out, const QServicePackage& package) +{ + const quint32 magicNumber = 0x78AFAFB; + out.setVersion(QDataStream::Qt_4_6); + out << magicNumber; + + const qint8 valid = package.d ? 1 : 0; + out << (qint8) valid; + if (valid) { + out << (qint8) package.d->packageType; + out << (qint8) package.d->responseType; + out << package.d->messageId; + out << package.d->instanceId; + out << package.d->entry; + out << package.d->payload; + } + + return out; +} + +QDataStream &operator>>(QDataStream &in, QServicePackage& package) +{ + const quint32 magicNumber = 0x78AFAFB; + in.setVersion(QDataStream::Qt_4_6); + + quint32 storedMagicNumber; + in >> storedMagicNumber; + if (storedMagicNumber != magicNumber) { + qWarning() << "Datastream doesn't provide serialized QServiceFilter"; + return in; + } + + qint8 valid; + in >> valid; + if (valid) { + if (!package.d) { + QServicePackagePrivate* priv = new QServicePackagePrivate(); + package.d = priv; + } else { + package.d.detach(); + package.d->clean(); + } + qint8 data; + in >> data; + package.d->packageType = (QServicePackage::Type) data; + in >> data; + package.d->responseType = (QServicePackage::ResponseType) data; + in >> package.d->messageId; + in >> package.d->instanceId; + in >> package.d->entry; + in >> package.d->payload; + } else { + if (package.d) + package.d.reset(); + } + + return in; +} +#endif + + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QServicePackage& p) +{ + if (p.isValid()) { + QString type; + switch (p.d->packageType) { + case QServicePackage::ObjectCreation: + type = QLatin1String("ObjectCreation"); + break; + case QServicePackage::MethodCall: + type = QLatin1String("MethodCall"); + break; + case QServicePackage::PropertyCall: + type = QLatin1String("PropertyCall"); + break; + default: + break; + } + dbg.nospace() << "QServicePackage "; + dbg.nospace() << type << " " << p.d->responseType ; dbg.space(); + dbg.nospace() << p.d->messageId; dbg.space(); + dbg.nospace() << p.d->entry;dbg.space(); + } else { + dbg.nospace() << "QServicePackage(invalid)"; + } + return dbg.space(); +} +#endif + + + +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/qservicepackage_p.h b/src/serviceframework/ipc/qservicepackage_p.h new file mode 100644 index 00000000..0801e437 --- /dev/null +++ b/src/serviceframework/ipc/qservicepackage_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICE_PACKAGE_H +#define QSERVICE_PACKAGE_H + +#include "qserviceframeworkglobal.h" +#include <QExplicitlySharedDataPointer> +#include <QSharedData> +#include <QUuid> +#include <QVariant> +#include "qremoteserviceregister.h" + +QT_BEGIN_NAMESPACE +class QDataStream; +class QDebug; +QT_END_NAMESPACE + +QTM_BEGIN_NAMESPACE + +class QServicePackagePrivate; +class Q_AUTOTEST_EXPORT QServicePackage +{ +public: + QServicePackage(); + QServicePackage(const QServicePackage& other); + QServicePackage& operator=(const QServicePackage& other); + ~QServicePackage(); + + enum Type { + ObjectCreation = 0, + MethodCall, + PropertyCall + }; + + enum ResponseType { + NotAResponse = 0, + Success, + Failed + }; + + QServicePackage createResponse() const; + + bool isValid() const; + + QExplicitlySharedDataPointer<QServicePackagePrivate> d; + +#ifndef QT_NO_DATASTREAM + friend QDataStream &operator<<(QDataStream &, const QServicePackage&); + friend QDataStream &operator>>(QDataStream &, QServicePackage&); +#endif +}; + +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &, const QServicePackage&); +QDataStream &operator>>(QDataStream &, QServicePackage&); +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug, const QServicePackage&); +#endif + +class QServicePackagePrivate : public QSharedData +{ +public: + QServicePackagePrivate() + : packageType(QServicePackage::ObjectCreation), + entry(QRemoteServiceRegister::Entry()), payload(QVariant()), + messageId(QUuid()), instanceId(QUuid()), responseType(QServicePackage::NotAResponse) + { + } + + QServicePackage::Type packageType; + QRemoteServiceRegister::Entry entry; + QVariant payload; + QUuid messageId; + QUuid instanceId; + QServicePackage::ResponseType responseType; + + virtual void clean() + { + packageType = QServicePackage::ObjectCreation; + messageId = QUuid(); + instanceId = QUuid(); + payload = QVariant(); + entry = QRemoteServiceRegister::Entry(); + responseType = QServicePackage::NotAResponse; + } +}; + +QTM_END_NAMESPACE + +#endif diff --git a/src/serviceframework/ipc/qsignalintercepter.cpp b/src/serviceframework/ipc/qsignalintercepter.cpp new file mode 100644 index 00000000..ba8a5ebf --- /dev/null +++ b/src/serviceframework/ipc/qsignalintercepter.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qsignalintercepter_p.h" +#include <qmetaobject.h> +#include <qmetatype.h> +#include <qobjectdefs.h> +#include <qdebug.h> + +/*! + \class QSignalIntercepter + \inpublicgroup QtBaseModule + \internal + + \brief The QSignalIntercepter class provides an interface for intercepting signals as meta-calls + \since 1.1 + + IPC mechanisms need to intercept signals and convert them into protocol + messages, but it is generally impractical to create a slot for every + signal that needs to be dispatched. The QSignalIntercepter class allows + signals to be intercepted as meta-calls so that IPC dispatching can + be implemented in a generic fashion. + + The activated() method is called whenever the signal is emitted, + with the arguments in a typed list. + + \sa QSlotInvoker + + \ingroup objectmodel +*/ + +QTM_BEGIN_NAMESPACE + +class QSignalIntercepterPrivate +{ +public: + QObject *sender; + QByteArray signal; + int signalIndex; + int destroyIndex; + int slotIndex; + int *types; + int numArgs; + + ~QSignalIntercepterPrivate() + { + if ( types ) + qFree( types ); + } +}; + +/*! + Create a new signal intercepter which traps \a signal on \a sender. + The object will be attached to \a parent, if present. + \since 1.1 +*/ +QSignalIntercepter::QSignalIntercepter + ( QObject *sender, const QByteArray& signal, QObject *parent ) + : QObject( parent ) +{ + // Initialize the private members. + d = new QSignalIntercepterPrivate(); + d->sender = sender; + d->signal = signal; + d->signalIndex = -1; + d->destroyIndex = -1; + d->slotIndex = -1; + d->types = 0; + + // Resolve the indices of the signals we are interested in. + if ( sender && signal.size() > 0 ) { + // '2' is QSIGNAL_CODE in Qt 4.4 and below, + // '6' is QSIGNAL_CODE in Qt 4.5 and higher. + if ( signal[0] != '2' && signal[0] != '6' ) { + qWarning( "QSignalIntercepter: `%s' is not a valid signal " + "specification", signal.constData() ); + return; + } + QByteArray name = QMetaObject::normalizedSignature + ( signal.constData() + 1 ); + d->signalIndex + = sender->metaObject()->indexOfSignal( name.constData() ); + if ( d->signalIndex < 0 ) { + qWarning( "QSignalIntercepter: no such signal: %s::%s", + sender->metaObject()->className(), signal.constData() ); + return; + } + d->destroyIndex + = sender->metaObject()->indexOfSignal( "destroyed()" ); + d->types = connectionTypes( name, d->numArgs ); + } + + // Derive a fake slot index to use in our manual qt_metacall implementation. + d->slotIndex = staticMetaObject.methodCount(); + + // Connect up the signals. + if ( d->signalIndex >= 0 ) { + QMetaObject::connect( sender, d->signalIndex, + this, d->slotIndex, + Qt::DirectConnection, 0 ); + } + if ( d->destroyIndex >= 0 ) { + QMetaObject::connect( sender, d->destroyIndex, + this, d->slotIndex + 1, + Qt::DirectConnection, 0 ); + } +} + +/*! + Destroy a signal intercepter. +*/ +QSignalIntercepter::~QSignalIntercepter() +{ + if ( d->signalIndex >= 0 ) { + QMetaObject::disconnect( d->sender, d->signalIndex, + this, d->slotIndex ); + } + if ( d->destroyIndex >= 0 ) { + QMetaObject::disconnect( d->sender, d->destroyIndex, + this, d->slotIndex + 1 ); + } + delete d; +} + +/*! + Returns the sender that this signal interceptor is attached to. + \since 1.1 +*/ +QObject *QSignalIntercepter::sender() const +{ + return d->sender; +} + +/*! + Returns the name of the signal that this signal interceptor is attached to. + \since 1.1 +*/ +QByteArray QSignalIntercepter::signal() const +{ + return d->signal; +} + +/*! + Returns true if this signal intercepter is valid; that is, there was + a signal present with the specified parameters when this object + was constructed. + \since 1.1 +*/ +bool QSignalIntercepter::isValid() const +{ + return ( d->signalIndex != -1 ); +} + +/*! + \internal + \since 1.1 +*/ +int QSignalIntercepter::qt_metacall(QMetaObject::Call c, int id, void **a) +{ + id = QObject::qt_metacall(c, id, a); + if (id < 0) + return id; + if (c == QMetaObject::InvokeMetaMethod) { + switch (id) { + case 0: { + // The signal we are interested in has been activated. + if ( d->types ) { + QList<QVariant> args; + for ( int i = 0; i < d->numArgs; ++i ) { + if ( d->types[i] != QVariantId ) { + QVariant arg( d->types[i], a[i + 1] ); + args.append( arg ); + } else { + args.append( *((const QVariant *)( a[i + 1] )) ); + } + } + activated( args ); + } + } + break; + + case 1: { + // The sender has been destroyed. Clear the signal indices + // so that we don't try to do a manual disconnect when our + // own destructor is called. + d->signalIndex = -1; + d->destroyIndex = -1; + } + break; + } + id -= 2; + } + return id; +} + +/*! + \fn void QSignalIntercepter::activated( const QList<QVariant>& args ) + + Called when the signal that is being intercepted is activated. + The arguments to the signal are passed in the list \a args. + \since 1.1 +*/ + +// Get the QVariant type number for a type name. +int QSignalIntercepter::typeFromName( const QByteArray& type ) +{ + int id; + if (type.endsWith('*')) + return QMetaType::VoidStar; + else if ( type.size() == 0 || type == "void" ) + return QMetaType::Void; + else if ( type == "QVariant" ) + return QSignalIntercepter::QVariantId; + id = QMetaType::type( type.constData() ); + if ( id != (int)QMetaType::Void ) + return id; + return QVariant::nameToType(type); +} + +/*! + Returns the connection types associated with a signal or slot \a member + specification. The array of types is returned from this function, + and the number of arguments is returned in \a nargs. Returns null + if \a member is invalid. The return value must be freed with qFree(). + \since 1.1 +*/ +int *QSignalIntercepter::connectionTypes( const QByteArray& member, int& nargs ) +{ + // Based on Qt's internal queuedConnectionTypes function. + nargs = 0; + int *types = 0; + const char *s = member.constData(); + while (*s != '\0' && *s != '(') { ++s; } + if ( *s == '\0' ) + return 0; + ++s; + const char *e = s; + while (*e != ')') { + ++e; + if (*e == ')' || *e == ',') + ++nargs; + } + + types = (int *) qMalloc((nargs+1)*sizeof(int)); + types[nargs] = 0; + for (int n = 0; n < nargs; ++n) { + e = s; + while (*s != ',' && *s != ')') + ++s; + QByteArray type(e, s-e); + ++s; + + types[n] = typeFromName(type); + if (!types[n]) { + qWarning("QSignalIntercepter::connectionTypes: Cannot marshal arguments of type '%s'", type.data()); + qFree(types); + return 0; + } + } + return types; +} + +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/qsignalintercepter_p.h b/src/serviceframework/ipc/qsignalintercepter_p.h new file mode 100644 index 00000000..c2661c9f --- /dev/null +++ b/src/serviceframework/ipc/qsignalintercepter_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QSIGNALINTERCEPTER_H +#define QSIGNALINTERCEPTER_H + +#include "qserviceframeworkglobal.h" +#include <qobject.h> +#include <qvariant.h> +#include <qlist.h> + +QT_BEGIN_HEADER + +QTM_BEGIN_NAMESPACE + +class QSignalIntercepterPrivate; +class QSignalIntercepter : public QObject +{ + // Note: Do not put Q_OBJECT here. + friend class QSlotInvoker; + friend class QCopProxy; +public: + QSignalIntercepter( QObject *sender, const QByteArray& signal, + QObject *parent=0 ); + ~QSignalIntercepter(); + + QObject *sender() const; + QByteArray signal() const; + + bool isValid() const; + + static const int QVariantId = -243; + + static int *connectionTypes( const QByteArray& member, int& nargs ); + +protected: + int qt_metacall( QMetaObject::Call c, int id, void **a ); + virtual void activated( const QList<QVariant>& args ) = 0; + +private: + QSignalIntercepterPrivate *d; + + static int typeFromName( const QByteArray& name ); +}; + +QTM_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/serviceframework/ipc/qslotinvoker.cpp b/src/serviceframework/ipc/qslotinvoker.cpp new file mode 100644 index 00000000..3c1bb456 --- /dev/null +++ b/src/serviceframework/ipc/qslotinvoker.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qslotinvoker_p.h" +#include "qsignalintercepter_p.h" +#include <qmetaobject.h> +#include <qmetatype.h> +#include <qvarlengtharray.h> + +/*! + \class QSlotInvoker + \inpublicgroup QtBaseModule + \internal + + \brief The QSlotInvoker class provides an interface for invoking slots with explicit arguments + \since 1.1 + + IPC mechanisms need to intercept protocol messages and convert them into + slot invocations, but it is generally impractical to create explicit code + for every slot that needs to be dispatched. The QSlotInvoker class allows + an IPC dispatching mechanism to invoke slots in a generic fashion using + the invoke() method. + + Methods that are marked with Q_INVOKABLE or Q_SCRIPTABLE can also + be invoked with this class. + + \sa QSignalIntercepter + + \ingroup objectmodel +*/ + +QTM_BEGIN_NAMESPACE + +class QSlotInvokerPrivate +{ +public: + QObject *receiver; + QByteArray member; + int memberIndex; + bool destroyed; + int returnType; + int *types; + int numArgs; + + ~QSlotInvokerPrivate() + { + if ( types ) + qFree( types ); + } +}; + +/*! + Create a slot invoker that can invoke \a member on \a receiver. + The object will be attached to \a parent, if present. + \since 1.1 +*/ +QSlotInvoker::QSlotInvoker( QObject *receiver, const QByteArray &member, + QObject *parent ) + : QObject( parent ) +{ + d = new QSlotInvokerPrivate(); + d->receiver = receiver; + QByteArray name; + if ( member.size() > 0 && member[0] >= '0' && member[0] <= '9' ) { + // Strip off the member type code. + name = member.mid(1); + } else { + name = member; + } + name = QMetaObject::normalizedSignature( name.constData() ); + d->member = name; + d->destroyed = false; + d->returnType = 0; + d->types = 0; + d->numArgs = 0; + if ( receiver && name.size() > 0 ) { + d->memberIndex + = receiver->metaObject()->indexOfMethod( name.constData() ); + } else { + d->memberIndex = -1; + } + if ( d->memberIndex != -1 ) { + QMetaMethod method = receiver->metaObject()->method + ( d->memberIndex ); + { + connect( receiver, SIGNAL(destroyed()), + this, SLOT(receiverDestroyed()) ); + d->returnType = + QSignalIntercepter::typeFromName( method.typeName() ); + d->types = QSignalIntercepter::connectionTypes + ( name, d->numArgs ); + if ( !( d->types ) ) + d->destroyed = true; + } + } else { + d->destroyed = true; + } +} + +/*! + Destroy a slot invoker. +*/ +QSlotInvoker::~QSlotInvoker() +{ + delete d; +} + +/*! + Returns true if the member is present on the object. + \since 1.1 +*/ +bool QSlotInvoker::memberPresent() const +{ + return ! d->destroyed; +} + +/*! + Returns true if the member can be invoked with \a numArgs arguments. + That is, the receiver has not been destroyed, the member is present, + and it requires \a numArgs or less arguments. + \since 1.1 +*/ +bool QSlotInvoker::canInvoke( int numArgs ) const +{ + if ( d->destroyed ) + return false; + return ( numArgs >= d->numArgs ); +} + +/*! + Returns the object that will receive slot invocations. + \since 1.1 +*/ +QObject *QSlotInvoker::receiver() const +{ + return d->receiver; +} + +/*! + Returns the member that will receiver slot invocations. + \since 1.1 +*/ +QByteArray QSlotInvoker::member() const +{ + return d->member; +} + +/*! + Returns the parameter types associated with this member. + \since 1.1 +*/ +int *QSlotInvoker::parameterTypes() const +{ + return d->types; +} + +/*! + Returns the number of parameter types associated with this member. + \since 1.1 +*/ +int QSlotInvoker::parameterTypesCount() const +{ + return d->numArgs; +} + +/*! + Invokes the slot represented by this object with the argument + list \a args. The slot's return value is returned from + this method. If the slot's return type is "void", then a + QVariant instance of type QVariant::Invalid will be returned. + + If it is possible that the slot may throw an exception, + it is the responsibility of the caller to catch and + handle the exception. + \since 1.1 +*/ +QVariant QSlotInvoker::invoke( const QList<QVariant>& args ) +{ + int arg; + QVariant returnValue; + + // Create a default instance of the return type for the result buffer. + if ( d->returnType != (int)QVariant::Invalid ) { + returnValue = QVariant( d->returnType, (const void *)0 ); + } + + // Bail out if the receiver object has already disappeared. + if ( d->destroyed ) + return returnValue; + + // Check that the number of arguments is compatible with the slot. + int numArgs = args.size(); + if ( numArgs < d->numArgs ) { + qWarning( "QSlotInvoker::invoke: insufficient arguments for slot" ); + return returnValue; + } else if ( numArgs > d->numArgs ) { + // Drop extraneous arguments. + numArgs = d->numArgs; + } + + // Construct the raw argument list. + QVarLengthArray<void *, 32> a( numArgs + 1 ); + if ( d->returnType == (int)QVariant::Invalid ) + a[0] = 0; + else + a[0] = returnValue.data(); + for ( arg = 0; arg < numArgs; ++arg ) { + if ( d->types[arg] == QSignalIntercepter::QVariantId ) { + a[arg + 1] = (void *)&( args[arg] ); + } else if ( args[arg].userType() != d->types[arg] ) { + qWarning( "QSlotInvoker::invoke: argument %d has incorrect type", + arg ); + return QVariant(); + } else { + a[arg + 1] = (void *)( args[arg].data() ); + } + } + + // Invoke the specified slot. + d->receiver->qt_metacall( QMetaObject::InvokeMetaMethod, + d->memberIndex, a.data() ); + return returnValue; +} + +void QSlotInvoker::receiverDestroyed() +{ + d->destroyed = true; +} + +#include "moc_qslotinvoker_p.cpp" + +QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/qslotinvoker_p.h b/src/serviceframework/ipc/qslotinvoker_p.h new file mode 100644 index 00000000..69b3c623 --- /dev/null +++ b/src/serviceframework/ipc/qslotinvoker_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QSLOTINVOKER_H +#define QSLOTINVOKER_H + +#include "qserviceframeworkglobal.h" +#include <qobject.h> +#include <qvariant.h> +#include <qlist.h> + +QT_BEGIN_HEADER + +QTM_BEGIN_NAMESPACE +class QSlotInvokerPrivate; +class QSlotInvoker : public QObject +{ + Q_OBJECT +public: + QSlotInvoker( QObject *receiver, const QByteArray& member, + QObject *parent=0 ); + ~QSlotInvoker(); + + bool memberPresent() const; + bool canInvoke( int numArgs ) const; + QObject *receiver() const; + QByteArray member() const; + int *parameterTypes() const; + int parameterTypesCount() const; + +public slots: + QVariant invoke( const QList<QVariant>& args ); + +private slots: + void receiverDestroyed(); + +private: + QSlotInvokerPrivate *d; +}; + +QTM_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/serviceframework/qabstractsecuritysession.cpp b/src/serviceframework/qabstractsecuritysession.cpp new file mode 100644 index 00000000..d68a5c40 --- /dev/null +++ b/src/serviceframework/qabstractsecuritysession.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qabstractsecuritysession.h" + +QTM_BEGIN_NAMESPACE + +/*! + \class QAbstractSecuritySession + \inmodule QtServiceFramework + \ingroup servicefw + \brief The QAbstractSecuritySession class provides a generic mechanism to enable + permission checks for services. + \since 1.0 + + QAbstractSecuritySession describes the abstract interface that security/permission + engines must implement in order to provide capability related functionality. + + A QAbstractSecuritySession encapsulates the service client's capabilities. QServiceManager + can match those capabilites with the capabilites required by a particular service. + Service capabilites are declared via the services XML description. + + The use of a security session is not mandated by the service manager. If the client + is passing a security session object QServiceManager ensures that the permissions + are checked before the requested service is loaded and forwards the session to the + service in case the service intends to implement additional checks. If no security + session is passed to QServiceManager capability checks are not performed. Note that + the security session is no substitute for platform security such as control over + a processes ability to load arbitrary plug-ins. + + Since the service loader controls whether a security session is passed to the + QServiceManager instance it is assumed that the calling context can be trusted. Possible + use cases for a security session could be arbitrary Javascript applications which run + within a trusted browser environment. The QAbstractSecuritySession interface would allow + the browser to provide access to platform services while at the same time being able to + ensure that certain Javascript application (depending on e.g their context, URL or signatures) + can not access more sensitive system services. + + Framework clients with purely native code bases are likely to never have any security sessions. + + \sa QServiceManager, QServicePluginInterface +*/ + +/*! + Constructs an abstract security session with the given \a parent. +*/ +QAbstractSecuritySession::QAbstractSecuritySession(QObject* parent) + : QObject(parent) +{ +} + +/*! + Destroys the abstract security session. +*/ +QAbstractSecuritySession::~QAbstractSecuritySession() +{ +} + +/*! + \fn bool QAbstractSecuritySession::isAllowed(const QStringList& capabilities) = 0; + + Returns true if the security session has sufficient rights to access the required + service \a capabilities. + \since 1.0 +*/ + +#include "moc_qabstractsecuritysession.cpp" + +QTM_END_NAMESPACE diff --git a/src/serviceframework/qabstractsecuritysession.h b/src/serviceframework/qabstractsecuritysession.h new file mode 100644 index 00000000..a33c39d6 --- /dev/null +++ b/src/serviceframework/qabstractsecuritysession.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTSECURITYSESSION_H +#define QABSTRACTSECURITYSESSION_H + +#include "qserviceframeworkglobal.h" +#include <QObject> + +QT_BEGIN_HEADER + +QTM_BEGIN_NAMESPACE + +class Q_SERVICEFW_EXPORT QAbstractSecuritySession : public QObject +{ + Q_OBJECT +public: + QAbstractSecuritySession(QObject* parent = 0); + virtual ~QAbstractSecuritySession(); + + virtual bool isAllowed(const QStringList& capabilityList) = 0; +}; + +QTM_END_NAMESPACE + +QT_END_HEADER + +#endif //QABSTRACTSECURITYSESSION_H + diff --git a/src/serviceframework/qremoteserviceregister.cpp b/src/serviceframework/qremoteserviceregister.cpp new file mode 100644 index 00000000..0ce330f3 --- /dev/null +++ b/src/serviceframework/qremoteserviceregister.cpp @@ -0,0 +1,461 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteserviceregister.h" +#include "qremoteserviceregisterentry_p.h" +#include "ipc/instancemanager_p.h" +#include "qremoteserviceregister_p.h" + +QTM_BEGIN_NAMESPACE + +/*! + \class QRemoteServiceRegister::Entry + \ingroup servicefw + \inmodule QtServiceFramework + \brief The Entry class represents a remote service entry to be published on QRemoteServiceRegister. + + This class is created using QRemoteServiceRegister::createEntry to supply remote service + details matching a valid QServiceInterfaceDescriptor. + + A registration entry can then be published for discovery by remote clients. + + \since 1.1 +*/ + +/*! + Constructs a null registration entry. + \since 1.1 +*/ +QRemoteServiceRegister::Entry::Entry() +{ + d = new QRemoteServiceRegisterEntryPrivate; +} + +/*! + Constructs the registration entry that is a copy of \a other. + \since 1.1 +*/ +QRemoteServiceRegister::Entry::Entry(const Entry& other) + : d(other.d) +{ +} + +/*! + Destroys the registration entry. + \since 1.1 +*/ +QRemoteServiceRegister::Entry::~Entry() +{ +} + +/*! + Checks if the registration entry is currently a valid remote service entry + + Returns true if the serviceName(), interfaceName() and version() point to + a valid QServiceInterfaceDescriptor, otherwise false. + \since 1.1 +*/ +bool QRemoteServiceRegister::Entry::isValid() const +{ + if (!d->iface.isEmpty() && !d->service.isEmpty() + && !d->ifaceVersion.isEmpty() && d->cptr!=0 && d->meta!=0) + return true; + return false; +} + +/*! + Returns true if this font is equal to \a other; otherwise false. + \since 1.1 +*/ +bool QRemoteServiceRegister::Entry::operator==(const Entry& other) const +{ + return d->service == other.d->service && + d->iface == other.d->iface && + d->ifaceVersion == other.d->ifaceVersion; +} + +/*! + Returns true if this font is different from \a other; otherwise false. + \since 1.1 +*/ +bool QRemoteServiceRegister::Entry::operator!=(const Entry& other) const +{ + return !(other == *this); +} + +/*! + Assigns \a other to this registration entry and returns a reference to it. + \since 1.1 +*/ +QRemoteServiceRegister::Entry &QRemoteServiceRegister::Entry::operator=(const Entry& other) +{ + d = other.d; + return *this; +} + +/*! + Returns the interface name of the registration entry. + + This should correspond to the interface name from the service XML description. + + \sa serviceName(), version() + \since 1.1 +*/ +QString QRemoteServiceRegister::Entry::interfaceName() const +{ + return d->iface; +} + +/*! + Returns the service name of the registration entry. + + This should correspond to the service name from the service XML description. + + \sa interfaceName(), version() + \since 1.1 +*/ +QString QRemoteServiceRegister::Entry::serviceName() const +{ + return d->service; +} + +/*! + Returns the version of the registration entry in format x.y. + + This should correspond to the interface version from the service XML description. + + \sa interfaceName(), serviceName() + \since 1.1 +*/ +QString QRemoteServiceRegister::Entry::version() const +{ + return d->ifaceVersion; +} + +const QMetaObject * QRemoteServiceRegister::Entry::metaObject() const +{ + return d->meta; +} + +/*! + Sets the QRemoteServiceRegister::InstanceType of the registration entry. + + If this is not explicitly called, the default instance \a type for the registration entry + is QRemoteServiceRegister::PrivateInstance. + \since 1.1 +*/ +void QRemoteServiceRegister::Entry::setInstantiationType(QRemoteServiceRegister::InstanceType type) +{ + d->instanceType = type; +} + +/*! + Returns the QRemoteServiceRegister::InstanceType of the registration entry. + \since 1.1 +*/ +QRemoteServiceRegister::InstanceType QRemoteServiceRegister::Entry::instantiationType() const +{ + return d->instanceType; +} + +/*! + \class QRemoteServiceRegister + \inmodule QtServiceFramework + \ingroup servicefw + \brief The QRemoteServiceRegister class manages instances of remote service objects. + \since 1.1 + + This class registers and publishes IPC based service objects. It owns the service's + objects and uess the platform specific IPC mechanism to publish the service. + + In order for the remote services to be discoverable by QServiceManager each + QRemoteServiceRegister::Entry must be registered with the same information in + the XML description, otherwise no corresponding QServiceInterfaceDescriptor can be + found. + + The following XML descriptor is used for subsequent examples. + + \code + <SFW version="1.1"> + <service> + <name>MyService</name> + <ipcaddress>my_executable</ipcaddress> + <description>My service example</description> + <interface> + <name>com.nokia.qt.example.myService</name> + <version>1.0</version> + <description>My private service</description> + <capabilities></capabilities> + </interface> + </service> + </SFW> + \endcode + + The snippet belows demonstrates how an application can register the class \a MyClass + as a remote service, which is published and accessible to clients who wish to load + service object instances. + + \code + int main(int argc, char** argv) + { + QCoreApplication app(argc, argv); + + QRemoteServiceRegister *serviceRegister = new QRemoteServiceRegister(); + + QRemoteServiceRegister::Entry myService; + myService = serviceRegister->createEntry<MyClass>( + "MyService", "com.nokia.qt.example.myservice", "1.0"); + + serviceRegister->publishEntries("my_service"); + + return app.exec(); + delete serviceRegister; + } + \endcode + + By default all entries are created as \l QRemoteServiceRegister::GlobalInstance + types. This can be changed by calling QRemoteServiceRegister::Entry::setInstantiationType() + on the entry. Once the service register has been published the associated service entries + can no longer be changed. + + \sa QRemoteServiceRegister::Entry +*/ + +/*! + \enum QRemoteServiceRegister::InstanceType + Defines the two types of instances for a registration entry + \value GlobalInstance New requests for a service gets the same service instance + \value PrivateInstance New requests for a service gets a new service instance +*/ + +/*! + \fn void QRemoteServiceRegister::instanceClosed(const QRemoteServiceRegister::Entry& entry) + + This signal is emitted whenever a created instance has been closed. This indicates + that a connected client has either shutdown or released the loaded service object. + + \a entry is supplied to identify which registered service + entry the closed instance belonged to. + + \sa allInstancesClosed() + \since 1.1 +*/ + +/*! + \fn void QRemoteServiceRegister::allInstancesClosed() + + This signal is emitted whenever all service instances have been closed. This indicates + that the last connected client has either shutdown or released the loaded service object. + + \sa instanceClosed() + \since 1.1 +*/ + +/*! + \typedef QRemoteServiceRegister::CreateServiceFunc + \internal + Denotes a function pointer returning a service instance +*/ + +/*! + \typedef QRemoteServiceRegister::SecurityFilter + \internal + Denotes a function pointer used for the security filter feature +*/ + +/*! + Creates a service register instance with the given \a parent. + \since 1.1 +*/ +QRemoteServiceRegister::QRemoteServiceRegister(QObject* parent) + : QObject(parent) +{ + d = QRemoteServiceRegisterPrivate::constructPrivateObject(this); + + connect(InstanceManager::instance(), SIGNAL(allInstancesClosed()), + this, SIGNAL(allInstancesClosed())); + connect(InstanceManager::instance(), SIGNAL(instanceClosed(QRemoteServiceRegister::Entry)), + this, SIGNAL(instanceClosed(QRemoteServiceRegister::Entry))); +} + +/*! + Destroys the service register instance +*/ +QRemoteServiceRegister::~QRemoteServiceRegister() +{ +} + +/*! + Publishes every service QRemoteServiceRegister::Entry that has been created using + \l createEntry(). The \a ident is the service specific IPC address under which + the service can be reached. + + This address must match the address provided in the services XML descriptor, otherwise + the service will not be discoverable. In some cases this may also cause the IPC + rendezvous feature to fail. + + \sa createEntry() + \since 1.1 +*/ +void QRemoteServiceRegister::publishEntries(const QString& ident) +{ + d->publishServices(ident); +} + +/*! + \property QRemoteServiceRegister::quitOnLastInstanceClosed + + \brief Terminate the service when all clients have closed all objects. Default value is true. + \since 1.1 +*/ +bool QRemoteServiceRegister::quitOnLastInstanceClosed() const +{ + return d->quitOnLastInstanceClosed(); +} + +void QRemoteServiceRegister::setQuitOnLastInstanceClosed(bool quit) +{ + d->setQuitOnLastInstanceClosed(quit); +} + +/*! + Allows a security filter to be set which can access + QRemoteServiceRegister::QRemoteServiceRegisterCredentials. + + The \a filter is a function pointer where the function code implements possible + permission checks and returns true or false. If a connecting client fails the security + filter it will be denied access and unable to obtain a valid service instance. + + The following snippet is an example of how to use the security filter feature. + + \code + bool myFunction(const void *p) + { + const QRemoteServiceRegisterCredentials *cred = + (const struct QRemoteServiceRegisterCredentials *)p; + + // allow the superuser + if (cred->uid == 0) + return true; + + return false; + } + + int main(int argc, char** argv) + { + ... + + QRemoteServiceRegister* serviceRegister = new QRemoteServiceRegister(); + service->setSecurityFilter(myFunction); + + ... + } + \endcode + + \since 1.1 +*/ +QRemoteServiceRegister::SecurityFilter QRemoteServiceRegister::setSecurityFilter(QRemoteServiceRegister::SecurityFilter filter) +{ + return d->setSecurityFilter(filter); +} + +/*! + \fn QRemoteServiceRegister::createEntry(const QString& serviceName, const QString& interfaceName, const QString& version) + + Creates an entry on our remote instance manager. The \a serviceName, \a interfaceName and + \a version must match the service XML descriptor in order for the remote service to be + discoverable. + + \sa publishEntries() + \since 1.1 +*/ +QRemoteServiceRegister::Entry QRemoteServiceRegister::createEntry(const QString& serviceName, const QString& interfaceName, const QString& version, QRemoteServiceRegister::CreateServiceFunc cptr, const QMetaObject* meta) +{ + if (serviceName.isEmpty() + || interfaceName.isEmpty() + || version.isEmpty() ) { + qWarning() << "QRemoteServiceRegister::registerService: service name, interface name and version must be specified"; + return Entry(); + } + + Entry e; + e.d->service = serviceName; + e.d->iface = interfaceName; + e.d->ifaceVersion = version; + e.d->cptr = cptr; + e.d->meta = meta; + + Q_ASSERT(InstanceManager::instance()); + InstanceManager::instance()->addType(e); + + return e; +} + + +#ifndef QT_NO_DATASTREAM +QDataStream& operator>>(QDataStream& s, QRemoteServiceRegister::Entry& entry) { + //for now we only serialize version, iface and service name + //needs to sync with qHash and operator== + s >> entry.d->service >> entry.d->iface >> entry.d->ifaceVersion; + return s; +} + +QDataStream& operator<<(QDataStream& s, const QRemoteServiceRegister::Entry& entry) { + //for now we only serialize version, iface and service name + //needs to sync with qHash and operator== + s << entry.d->service << entry.d->iface << entry.d->ifaceVersion; + return s; +} +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRemoteServiceRegister::Entry& entry) { + dbg.nospace() << "QRemoteServiceRegister::Entry(" + << entry.serviceName() << ", " + << entry.interfaceName() << ", " + << entry.version() << ")"; + return dbg.space(); +} +#endif + +#include "moc_qremoteserviceregister.cpp" + +QTM_END_NAMESPACE diff --git a/src/serviceframework/qremoteserviceregister.h b/src/serviceframework/qremoteserviceregister.h new file mode 100644 index 00000000..aa8e6756 --- /dev/null +++ b/src/serviceframework/qremoteserviceregister.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTESERVICEREGISTER_H +#define QREMOTESERVICEREGISTER_H + +#include "qserviceframeworkglobal.h" +#include <QObject> +#include <QQueue> +#include <QHash> +#include <QDebug> +#include <QExplicitlySharedDataPointer> + +QTM_BEGIN_NAMESPACE + +class QRemoteServiceRegisterPrivate; +class QRemoteServiceRegisterEntryPrivate; + +class Q_SERVICEFW_EXPORT QRemoteServiceRegister : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool quitOnLastInstanceClosed READ quitOnLastInstanceClosed WRITE setQuitOnLastInstanceClosed) +public: + + enum InstanceType { + GlobalInstance = 0, + PrivateInstance + }; + + typedef QObject *(*CreateServiceFunc)(); + + class Q_SERVICEFW_EXPORT Entry { + public: + Entry(); + Entry(const Entry &); + Entry &operator=(const Entry &); + + ~Entry(); + + bool operator==(const Entry &) const; + bool operator!=(const Entry &) const; + + bool isValid() const; + + QString interfaceName() const; + QString serviceName() const; + QString version() const; + + void setInstantiationType(QRemoteServiceRegister::InstanceType type); + QRemoteServiceRegister::InstanceType instantiationType() const; + + private: + QExplicitlySharedDataPointer<QRemoteServiceRegisterEntryPrivate> d; + + const QMetaObject* metaObject() const; + + friend class QRemoteServiceRegister; + friend class InstanceManager; + friend class QServiceManager; +#ifndef QT_NO_DATASTREAM + friend Q_SERVICEFW_EXPORT QDataStream &operator<<(QDataStream &, const QRemoteServiceRegister::Entry &); + friend Q_SERVICEFW_EXPORT QDataStream &operator>>(QDataStream &, QRemoteServiceRegister::Entry &); +#endif + }; + + QRemoteServiceRegister(QObject* parent = 0); + ~QRemoteServiceRegister(); + + template <typename T> + Entry createEntry(const QString& serviceName, + const QString& interfaceName, const QString& version); + + void publishEntries(const QString& ident ); + + bool quitOnLastInstanceClosed() const; + void setQuitOnLastInstanceClosed(const bool quit); + + typedef bool (*SecurityFilter)(const void *message); + SecurityFilter setSecurityFilter(SecurityFilter filter); + +Q_SIGNALS: + void allInstancesClosed(); + void instanceClosed(const QRemoteServiceRegister::Entry& entry); + +private: + + Entry createEntry(const QString& serviceName, + const QString& interfaceName, const QString& version, + CreateServiceFunc cptr, const QMetaObject* meta); + + QRemoteServiceRegisterPrivate* d; +}; + +struct QRemoteServiceRegisterCredentials { + int fd; + int pid; + int uid; + int gid; +}; + +inline uint qHash(const QRemoteServiceRegister::Entry& e) { + //Only consider version, iface and service name -> needs to sync with operator== + return ( qHash(e.serviceName()) + qHash(e.interfaceName()) + qHash(e.version()) ); +} + +#ifndef QT_NO_DATASTREAM +QDataStream& operator>>(QDataStream& s, QRemoteServiceRegister::Entry& entry); +QDataStream& operator<<(QDataStream& s, const QRemoteServiceRegister::Entry& entry); +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRemoteServiceRegister::Entry& entry); +#endif + +template <typename T> +QObject* qServiceTypeConstructHelper() +{ + return new T; +} + +template <typename T> +QRemoteServiceRegister::Entry QRemoteServiceRegister::createEntry(const QString& serviceName, + const QString& interfaceName, const QString& version) +{ + + QRemoteServiceRegister::CreateServiceFunc cptr = qServiceTypeConstructHelper<T>; + return createEntry(serviceName, interfaceName, version, cptr, &T::staticMetaObject); +} + + +QTM_END_NAMESPACE +#endif //QREMOTESERVICEREGISTER_H diff --git a/src/serviceframework/qservice.h b/src/serviceframework/qservice.h new file mode 100644 index 00000000..e7d5fdf3 --- /dev/null +++ b/src/serviceframework/qservice.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q_SERVICE_H +#define Q_SERVICE_H + +#include "qserviceframeworkglobal.h" + +QT_BEGIN_HEADER + +QTM_BEGIN_NAMESPACE + +//QTM_SYNC_HEADER_EXPORT QService +namespace QService +{ + enum Scope { + UserScope = 0, + SystemScope + }; + + enum UnrecoverableIPCError { + ErrorUnknown = 0, + ErrorServiceNoLongerAvailable, + ErrorOutofMemory, + ErrorPermissionDenied, + ErrorInvalidArguments + }; + + enum Type { + Plugin = 0, + InterProcess + }; +} + +QTM_END_NAMESPACE + +QT_END_HEADER +#endif //Q_SERVICE_H diff --git a/src/serviceframework/qservice.qdoc b/src/serviceframework/qservice.qdoc new file mode 100644 index 00000000..d1d8ef6c --- /dev/null +++ b/src/serviceframework/qservice.qdoc @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \namespace QService + \ingroup servicefw + \inmodule QtServiceFramework + + \brief The QService namespace contains miscellaneous identifiers used throughout the + Qt Service framework library. +*/ + +/*! + \enum QService::Scope + Defines the scope to be used when accessing services. Note that Symbian + does not distinguish scopes and therefore UserScope and SystemScope may + be used interchangeably. + + \value UserScope When adding and removing services, uses a storage location + specific to the current user. + When searching for services and interface implementations, first searches in the + user-specific location; if the service or interface implementation + is not found, searches in the system-wide storage location (if the user has + sufficient permissions to do so). + + \value SystemScope When adding and removing services, use a system-wide + storage location accessible to all users. When searching + for services and interface implementations, search only in the system-wide + storage location. +*/ + +/*! + \enum QService::UnrecoverableIPCError + Defines the unrecoverable IPC error of the service + + \value ErrorUnknown An unknown IPC error. + \value ErrorServiceNoLongerAvailable Indicates that the service is no longer available. + \value ErrorOutofMemory Indicates that the service is out of memoruy. + \value ErrorPermissionDenied Indicates that the permission of this service is denied. + \value ErrorInvalidArguments User uses invalid argument for this service. +*/ + +/*! + \enum QService::Type + Defines the type of the service + + \value Plugin This denotes that the service is plug-in based. + \value InterProcess This denotes that the service is deployed using IPC mechanisms + available on the current platform, such as DBus or local sockets. +*/ diff --git a/src/serviceframework/qservicecontext.cpp b/src/serviceframework/qservicecontext.cpp new file mode 100644 index 00000000..cfef3652 --- /dev/null +++ b/src/serviceframework/qservicecontext.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qservicecontext.h" + +QTM_BEGIN_NAMESPACE + +#define CLIENT_DATA_INDEX 0 + +class ServiceContextClientData : public QObjectUserData +{ +public: + virtual ~ServiceContextClientData() {} + + QHash<QString, QVariant> clientData; +}; + + +/*! + \class QServiceContext + \inmodule QtServiceFramework + \ingroup servicefw + \brief The QServiceContext class provides context information to + services. + \since 1.0 + + A service context is created by clients and passed on to the service. + It enables the opportunity to pass additional context information + and errors between services, clients and the service framework. + + Clients must implement this abstract class to receive context information. + + \sa QServiceManager + +*/ + +/*! + \enum QServiceContext::ContextType + + This enum describes the type of context information. + + \value DisplayContext The service provides user visible display + text such as an error message. + \value ScriptContext The service provides a script which may + be executed by the client. + \value UserDefined The first context type that can be used for service + specific context information. +*/ + +/*! + \fn void QServiceContext::notify(ContextType type, const QVariant& data) = 0 + + Services may call this function to notify the service client about service related + context information of the given \a type. The contextual information is stored in \a data. + \since 1.0 +*/ + + +/*! + Constructs a service context with the given \a parent. +*/ +QServiceContext::QServiceContext(QObject* parent) + : QObject(parent) +{ +#ifndef QT_NO_USERDATA + //Ideally we would use a new data member to store the client information. + //However since a d-pointer was forgotten when designing QServiceContext + //we need to fall back to QObject user data. + ServiceContextClientData* data = new ServiceContextClientData(); + setUserData(CLIENT_DATA_INDEX, data); +#endif +} + +/*! + Destroys the service context object. +*/ +QServiceContext::~QServiceContext() +{ + //ServiceContextUserData deleted by QObject +} + +/*! + \property QServiceContext::clientId + \brief the id of the client using the service. + + By default, this value is empty but you can change this by calling + setClientId(). + \since 1.0 +*/ +QString QServiceContext::clientId() const +{ + return m_id; +} + +/*! + Sets the \a id of the client using the service. + \since 1.0 +*/ +void QServiceContext::setClientId(const QString& id) +{ + m_id = id; +} + +/*! + \property QServiceContext::clientName + \brief the name of the client using the service. + + By default, this value is empty but you can change this by calling + setClientName(). This string is translated and can be shown to the user. + \since 1.0 +*/ +QString QServiceContext::clientName() const +{ + return m_displayName; +} + +void QServiceContext::setClientName(const QString& name) +{ + m_displayName = name; +} + +/*! + Returns the client data associated to \a key. + + \sa setClientData(), resetClientData() + \since 1.1 +*/ +QVariant QServiceContext::clientData(const QString& key) const +{ +#ifndef QT_NO_USERDATA + ServiceContextClientData* data = + static_cast<ServiceContextClientData*>(userData(CLIENT_DATA_INDEX)); + return data->clientData.value(key); +#else + return QVariant(); +#endif +} + +/*! + Attaches arbitrary data \a value to the context object. The value + can be retrieved via \a key. + + \sa clientData(), resetClientData() + \since 1.1 +*/ +void QServiceContext::setClientData(const QString& key, const QVariant& value) +{ +#ifndef QT_NO_USERDATA + ServiceContextClientData* data = + static_cast<ServiceContextClientData*>(userData(CLIENT_DATA_INDEX)); + data->clientData[key] = value; +#endif +} + +/*! + Deletes all client data associated to the service context. + + \sa clientData(), setClientData() + \since 1.1 +*/ +void QServiceContext::resetClientData() +{ +#ifndef QT_NO_USERDATA + ServiceContextClientData* data = + static_cast<ServiceContextClientData*>(userData(CLIENT_DATA_INDEX)); + data->clientData.clear(); +#endif +} + +#include "moc_qservicecontext.cpp" + +QTM_END_NAMESPACE diff --git a/src/serviceframework/qservicecontext.h b/src/serviceframework/qservicecontext.h new file mode 100644 index 00000000..8271317b --- /dev/null +++ b/src/serviceframework/qservicecontext.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICECONTEXT_H +#define QSERVICECONTEXT_H + +#include "qserviceframeworkglobal.h" +#include <QObject> +#include <QVariant> +#include <QString> + + +QT_BEGIN_HEADER + +QTM_BEGIN_NAMESPACE + +class Q_SERVICEFW_EXPORT QServiceContext : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString clientId READ clientId WRITE setClientId) + Q_PROPERTY(QString clientName READ clientName WRITE setClientName) +public: + enum ContextType { + DisplayContext = 0, + ScriptContext, + UserDefined = 100 + }; + + QServiceContext(QObject* parent = 0); + virtual ~QServiceContext(); + + virtual void notify( ContextType type, const QVariant& variant) = 0; + + + QString clientId() const; + void setClientId(const QString& clientId); + + QString clientName() const; + void setClientName(const QString& name); + + QVariant clientData(const QString& key) const; + void setClientData(const QString& key, const QVariant& value); + void resetClientData(); + +private: + QString m_id; + QString m_displayName; + +}; + +QTM_END_NAMESPACE + +QT_END_HEADER +#endif //QSERVICECONTEXT_H + diff --git a/src/serviceframework/qservicefilter.cpp b/src/serviceframework/qservicefilter.cpp new file mode 100644 index 00000000..84141cde --- /dev/null +++ b/src/serviceframework/qservicefilter.cpp @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QRegExp> +#include <QStringList> +#include <QDebug> +#ifndef QT_NO_DATASTREAM +#include <qdatastream.h> +#endif + +#include "qservicefilter.h" + +QTM_BEGIN_NAMESPACE + +class QServiceFilterPrivate +{ +public: + QString interface; + QString service; + int majorVersion; + int minorVersion; + QServiceFilter::VersionMatchRule matchingRule; + QHash<QString,QString> customAttributes; + QStringList capabilities; + QServiceFilter::CapabilityMatchRule capMatchingRule; +}; + + +/*! + \class QServiceFilter + + \ingroup servicefw + \inmodule QtServiceFramework + \brief The QServiceFilter class defines criteria for defining a sub-set of + all available services. + \since 1.0 + + A QServiceFilter can be used to constrain the number of services when searching + for services. Only those services that match all filter criteria are returned + by \l QServiceManager::findInterfaces(). + + + \sa QServiceInterfaceDescriptor, QServiceManager +*/ + +/*! + \enum QServiceFilter::VersionMatchRule + + This enum describes how interface version matching is performed. + + \value ExactVersionMatch The filter matches any interface implementation that implements + the exact version provided. + \value MinimumVersionMatch The filter matches any interface implementation that implements + either the given major/minor version or any subsequent version. +*/ + +/*! + \enum QServiceFilter::CapabilityMatchRule + + This enum describes the capability/permission matching rules. Some platforms restrict what services clients + can access using "capabilities" or permissions. Services with more capabilities require + more privileged clients. Platforms without capabilities may ignore this type of matching + rule as the default behavior is to ignore any capability restrictions. + + This is a brief example. Assuming that the system knows the services S1 - S6 which require capabilities as stated below: + \table + \header \o Service \o Required capabilities + \row \o S1 \o \{\} + \row \o S2 \o \{A\} + \row \o S3 \o \{A,B\} + \row \o S4 \o \{A,B,C,D\} + \row \o S5 \o \{A,D\} + \row \o S6 \o \{F\} + \endtable + + The matching rules would apply as follows: + + \table + \header \o Matching rule \o Filter's capabilities \o Matching services + \row \o MatchLoadable \o \{\} \o S1 + \row \o MatchLoadable \o \{A\} \o S1, S2 + \row \o MatchLoadable \o \{A,B,C\} \o S1, S2, S3 + \row \o MatchMinimum \o \{\} \o S1, S2, S3, S4, S5, S6 + \row \o MatchMinimum \o \{A\} \o S2, S3, S4, S5 + \row \o MatchMinimum \o \{A,B,C\} \o S4 + \endtable + + \value MatchMinimum The filter matches any service that requires at least the given + filter capabilities. This may mean that the returned services + may require more capabilities than the specified ones. + Such a search is equivalent to a wildcard match if the passed filter's capability list is empty. In mathematical set notation + this rule is equivalent to Cap\sub{(Filter)} \\ Cap\sub{(Service)} = {}. This is the default matching rule. + \value MatchLoadable The filter matches any service that could be loaded by the client. + Using this matching rule guarantees that the returned services do not + require more capabilites than specified by this rule. It includes services + with no capability requirements. If this rule + is provided alongside an empty capability search list the returned + services do not require any capabilities and thus can be accessed + by any client. The equivalent set notation is Cap\sub{(Service)} \\ Cap\sub{(Filter)} = {}. +*/ + +/*! + Creates a new filter object that matches all service implementations. +*/ +QServiceFilter::QServiceFilter() +{ + d = new QServiceFilterPrivate(); + d->majorVersion = -1; + d->minorVersion = -1; + d->matchingRule = QServiceFilter::MinimumVersionMatch; + d->capMatchingRule = QServiceFilter::MatchMinimum; +} + +/*! + Creates a copy of QServiceFilter object contained in \a other. +*/ +QServiceFilter::QServiceFilter(const QServiceFilter& other) +{ + d = new QServiceFilterPrivate(); + (*this) = other; +} + +/*! + \fn QServiceFilter::QServiceFilter(const QString& interfaceName, const QString& version, QServiceFilter::VersionMatchRule rule) + + Creates a new filter object that matches all service + implementations implementing \a interfaceName that match the specified + \a version using the given \a rule. + \since 1.0 +*/ +QServiceFilter::QServiceFilter(const QString& interfaceName, const QString& version, QServiceFilter::VersionMatchRule rule) +{ + d = new QServiceFilterPrivate(); + d->majorVersion = -1; + d->minorVersion = -1; + d->matchingRule = QServiceFilter::MinimumVersionMatch; + d->capMatchingRule = QServiceFilter::MatchMinimum; + setInterface(interfaceName, version, rule); +} + +/*! + Destroys this instance of QServiceFilter. +*/ +QServiceFilter::~QServiceFilter() +{ + delete d; +} + +/*! + \fn QServiceFilter& QServiceFilter::operator=(const QServiceFilter& other) + + Copies the content of the QServiceFilter object contained in + \a other into this one. + \since 1.0 +*/ +QServiceFilter& QServiceFilter::operator=(const QServiceFilter& other) +{ + d->interface = other.d->interface; + d->service = other.d->service; + d->majorVersion = other.d->majorVersion; + d->minorVersion = other.d->minorVersion; + d->matchingRule = other.d->matchingRule; + d->customAttributes = other.d->customAttributes; + d->capabilities = other.d->capabilities; + d->capMatchingRule = other.d->capMatchingRule; + + return *this; +} + +/*! + \fn void QServiceFilter::setServiceName(const QString& serviceName) + + The filter only matches implementations which are provided by the service + specified by \a serviceName. + + If the \a serviceName is empty the filter matches any service. + \since 1.0 +*/ +void QServiceFilter::setServiceName(const QString& serviceName) +{ + d->service = serviceName; +} + +/*! + \fn void QServiceFilter::setInterface(const QString &interfaceName, const QString& version, QServiceFilter::VersionMatchRule rule) + + Sets the filter to match any interface implementation that implements + \a interfaceName with version \a version. The version is matched + according to the given \a rule. If \a version is not set, the filter matches any version of the + interface implementation. + + This method does nothing if \a version is not a valid version string or + if \a interfaceName is empty. + + A valid version string has the format x.y whereby x and y are positive integer + numbers. + \since 1.0 +*/ +void QServiceFilter::setInterface(const QString &interfaceName, const QString& version, QServiceFilter::VersionMatchRule rule) +{ + //unset interface name + if (interfaceName.isEmpty() && version.isEmpty()) + { + d->interface = interfaceName; + d->majorVersion = d->minorVersion = -1; + d->matchingRule = rule; + return; + } + + if (interfaceName.isEmpty()) { + qWarning() << "Empty interface name. Ignoring filter details"; + return; + } + + if (version.isEmpty()) { + d->majorVersion = d->minorVersion = -1; + d->matchingRule = rule; + d->interface = interfaceName; + return; + } + + // Match x.y as version format. + // This differs from regex in servicemetadata in that 0.x versions are + // accepted for the search filter. + QRegExp rx(QLatin1String("^(0+|[1-9][0-9]*)\\.(0+|[1-9][0-9]*)$")); + int pos = rx.indexIn(version); + QStringList list = rx.capturedTexts(); + bool success = false; + int temp_major = -1; + int temp_minor = -1; + if (pos == 0 && list.count() == 3 + && rx.matchedLength() == version.length() ) + { + temp_major = list[1].toInt(&success); + if ( success ) { + temp_minor = list[2].toInt(&success); + } + } + + if (success) { + d->majorVersion = temp_major; + d->minorVersion = temp_minor; + d->interface = interfaceName; + d->matchingRule = rule; + } else { + qWarning() << "Invalid version tag" << version << ". Ignoring filter details."; + } +} + +/*! + \fn QString QServiceFilter::serviceName() const + + Returns the service name for this filter. + + \sa setServiceName() + \since 1.0 +*/ +QString QServiceFilter::serviceName() const +{ + return d->service; +} + +/*! + \fn QString QServiceFilter::interfaceName() const + + Returns the interface name for this filter. + + \sa setInterface() + \since 1.0 +*/ +QString QServiceFilter::interfaceName() const +{ + return d->interface; +} + +/*! + \fn int QServiceFilter::majorVersion() const + + Returns the major interface version for this filter. + + \sa setInterface() + \since 1.0 +*/ +int QServiceFilter::majorVersion() const +{ + return d->majorVersion; +} + +/*! + \fn int QServiceFilter::minorVersion() const + + Returns the minor interface version for this filter. + + \sa setInterface() + \since 1.0 +*/ +int QServiceFilter::minorVersion() const +{ + return d->minorVersion; +} + +/*! + \fn void QServiceFilter::setCustomAttribute(const QString& key, const QString& value) + + The filter only matches implementations which have the custom attribute + \a key with the given \a value. Such constraints are specified via the + \i{<customproperty>} tag within the service xml. + + \sa customAttribute(), clearCustomAttribute() + \since 1.0 +*/ +void QServiceFilter::setCustomAttribute(const QString& key, const QString& value) +{ + d->customAttributes.insert(key, value); +} + +/*! + \fn QString QServiceFilter::customAttribute(const QString& key) const + + Returns the value for the custom attribute \a key; otherwise + returns a null string. + + \sa setCustomAttribute(), clearCustomAttribute() + \since 1.0 +*/ +QString QServiceFilter::customAttribute(const QString& key) const +{ + return d->customAttributes.value(key); +} + +/*! + \fn void QServiceFilter::clearCustomAttribute(const QString &key) + + Clears the custom attribute \a key from the filter's set of constraints. + If \a key is empty all custom attributes are cleared. + + \sa setCustomAttribute() + \since 1.0 +*/ +void QServiceFilter::clearCustomAttribute(const QString &key) +{ + if (key.isEmpty()) + d->customAttributes.clear(); + else + d->customAttributes.remove(key); +} + +/*! + \fn QServiceFilter::VersionMatchRule QServiceFilter::versionMatchRule() const + + Returns the version match rule for this filter. + + \sa setInterface() + \since 1.0 +*/ +QServiceFilter::VersionMatchRule QServiceFilter::versionMatchRule() const +{ + return d->matchingRule; +} + +/*! + \fn QList<QString> QServiceFilter::customAttributes() const + + Returns the list of custom keys which have been added to the filter. + \since 1.0 +*/ +QStringList QServiceFilter::customAttributes() const +{ + return d->customAttributes.keys(); +} + +/*! + \fn void QServiceFilter::setCapabilities(QServiceFilter::CapabilityMatchRule rule, const QStringList& capabilities ) + + Sets the list of \a capabilities which are used to constrain + searches for services. The capabilities are matched according + to the given \a rule. + + \sa capabilities(), QAbstractSecuritySession + \since 1.0 +*/ +void QServiceFilter::setCapabilities(QServiceFilter::CapabilityMatchRule rule, const QStringList& capabilities ) +{ + d->capMatchingRule = rule; + d->capabilities = capabilities; +} + +/*! + \fn QStringList QServiceFilter::capabilities() const + + Returns the list of capabilities which are used to limit services searches. + + The filter matches any services that requires the given or less + capabilities and thus enabling clients to query for services + for which they have the required capabilties. + + \sa setCapabilities(), capabilityMatchRule(), QAbstractSecuritySession + \since 1.0 +*/ +QStringList QServiceFilter::capabilities() const +{ + return d->capabilities; +} + +/*! + Returns the capability matching rule for this filter. + + \sa setCapabilities(), capabilities() + \since 1.0 +*/ +QServiceFilter::CapabilityMatchRule QServiceFilter::capabilityMatchRule() const +{ + return d->capMatchingRule; +} + +#ifndef QT_NO_DATASTREAM +/*! + \fn QDataStream &operator<<(QDataStream &out, const QServiceFilter &sf) + \relates QServiceFilter + + Writes service filter \a sf to the stream \a out and returns a reference + to the stream. + \since 1.0 +*/ + +QDataStream &operator<<(QDataStream &out, const QServiceFilter &sf) +{ + const quint32 magicNumber = 0x78AFAFA; + const qint32 mj = sf.d->majorVersion; + const qint32 mn = sf.d->minorVersion; + const qint8 versionrule = (qint32) sf.d->matchingRule; + const qint8 caprule = (qint8) sf.d->capMatchingRule; + const quint16 majorVersion = 1; + const quint16 minorVersion = 0; + + out << magicNumber + << majorVersion + << minorVersion + << sf.d->interface + << sf.d->service + << mj + << mn + << versionrule + << sf.d->customAttributes + << caprule + << sf.d->capabilities; + return out; +} + +/*! + \fn QDataStream &operator>>(QDataStream &in, QServiceFilter &sf) + \relates QServiceFilter + + Reads a service filter into \a sf from the stream \a in and returns a + reference to the stream. + \since 1.0 +*/ +QDataStream &operator>>(QDataStream &in, QServiceFilter &sf) +{ + const quint32 magicNumber = 0x78AFAFA; + qint32 mj, mn; + qint8 versionrule, caprule; + + quint32 storedMagicNumber; + in >> storedMagicNumber; + if (storedMagicNumber != magicNumber) { + qWarning() << "Datastream doesn't provide serialized QServiceFilter"; + return in; + } + + const quint16 currentMajorVersion = 1; + quint16 majorVersion = 0; + quint16 minorVersion = 0; + + in >> majorVersion >> minorVersion; + if (majorVersion != currentMajorVersion) { + qWarning() << "Unknown serialization format for QServiceFilter."; + return in; + } + //Allow all minor versions. + + in >> sf.d->interface + >> sf.d->service + >> mj + >> mn + >> versionrule + >> sf.d->customAttributes + >> caprule + >> sf.d->capabilities; + + sf.d->majorVersion = mj; + sf.d->minorVersion = mn; + sf.d->matchingRule = (QServiceFilter::VersionMatchRule) versionrule; + sf.d->capMatchingRule = (QServiceFilter::CapabilityMatchRule) caprule; + + return in; +} +#endif //QT_NO_DATASTREAM + + +QTM_END_NAMESPACE + diff --git a/src/serviceframework/qservicefilter.h b/src/serviceframework/qservicefilter.h new file mode 100644 index 00000000..9a432ecb --- /dev/null +++ b/src/serviceframework/qservicefilter.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICEFILTER_H +#define QSERVICEFILTER_H + +#include "qserviceframeworkglobal.h" +#include <QStringList> + +QT_BEGIN_NAMESPACE +class QDataStream; +QT_END_NAMESPACE + +QT_BEGIN_HEADER + +QTM_BEGIN_NAMESPACE + +#ifdef QT_SFW_SERVICEDATABASE_GENERATE +#undef Q_SERVICEFW_EXPORT +#define Q_SERVICEFW_EXPORT +#endif + +class QServiceFilterPrivate; +class Q_SERVICEFW_EXPORT QServiceFilter +{ +public: + enum VersionMatchRule { + ExactVersionMatch = 0, + MinimumVersionMatch + }; + + enum CapabilityMatchRule { + MatchMinimum = 0, + MatchLoadable + }; + + QServiceFilter(); + ~QServiceFilter(); + QServiceFilter(const QServiceFilter& other); + explicit QServiceFilter(const QString& interfaceName, + const QString& version = QString(), + QServiceFilter::VersionMatchRule rule = QServiceFilter::MinimumVersionMatch); + + QServiceFilter& operator=(const QServiceFilter& other); + + void setInterface(const QString& interfaceName, const QString& version = QString(), + QServiceFilter::VersionMatchRule rule = QServiceFilter::MinimumVersionMatch); + void setServiceName(const QString& serviceName); + + + QString serviceName() const; + QString interfaceName() const; + int majorVersion() const; + int minorVersion() const; + VersionMatchRule versionMatchRule() const; + + QStringList customAttributes() const; + QString customAttribute(const QString& which) const; + void setCustomAttribute(const QString& key, const QString& value); + void clearCustomAttribute(const QString &key = QString()); + + void setCapabilities(QServiceFilter::CapabilityMatchRule, const QStringList& capabilities = QStringList() ); + QStringList capabilities() const; + CapabilityMatchRule capabilityMatchRule() const; + +private: + QServiceFilterPrivate *d; + friend class QServiceManager; + //friend class ServiceDatabase; +#ifndef QT_NO_DATASTREAM + friend Q_SERVICEFW_EXPORT QDataStream &operator<<(QDataStream &, const QServiceFilter &); + friend Q_SERVICEFW_EXPORT QDataStream &operator>>(QDataStream &, QServiceFilter &); +#endif +}; + +#ifndef QT_NO_DATASTREAM +Q_SERVICEFW_EXPORT QDataStream &operator<<(QDataStream &, const QServiceFilter &); +Q_SERVICEFW_EXPORT QDataStream &operator>>(QDataStream &, QServiceFilter &); +#endif + +QTM_END_NAMESPACE + +QT_END_HEADER +#endif //QSERVICEFILTER_H diff --git a/src/serviceframework/qserviceframeworkglobal.h b/src/serviceframework/qserviceframeworkglobal.h new file mode 100644 index 00000000..2a9382d3 --- /dev/null +++ b/src/serviceframework/qserviceframeworkglobal.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QSERVICEFRAMEWORK_H +#define QSERVICEFRAMEWORK_H + +#include <QtCore/qglobal.h> + +// The namespace is hardcoded as moc has issues resolving +// macros which would be a prerequisite for a dynmamic namespace +//#define QTM_NAMESPACE QtMobility +//#define QTM_NAMESPACE + +#if defined(Q_OS_WIN) +# if defined(QT_NODLL) +# undef QT_MAKEDLL +# undef QT_DLL +# elif defined(QT_MAKEDLL) +# if defined(QT_DLL) +# undef QT_DLL +# endif +# if defined(QT_BUILD_SFW_LIB) +# define Q_SERVICEFW_EXPORT Q_DECL_EXPORT +# else +# define Q_SERVICEFW_EXPORT Q_DECL_IMPORT +# endif +# elif defined(QT_DLL) +# define Q_SERVICEFW_EXPORT Q_DECL_EXPORT +# endif +#endif + +#if !defined(Q_SERVICEFW_EXPORT) +# if defined(QT_SHARED) +# define Q_SERVICEFW_EXPORT Q_DECL_EXPORT +# else +# define Q_SERVICEFW_EXPORT +# endif +#endif + +#ifdef QTM_NAMESPACE +# define QTM_PREPEND_NAMESPACE(name) ::QTM_NAMESPACE::name +# define QTM_BEGIN_NAMESPACE namespace QTM_NAMESPACE { +# define QTM_END_NAMESPACE } +# define QTM_USE_NAMESPACE using namespace QTM_NAMESPACE; +#else +# define QTM_PREPEND_NAMESPACE(name) ::name +# define QTM_BEGIN_NAMESPACE +# define QTM_END_NAMESPACE +# define QTM_USE_NAMESPACE +#endif + +//in case Qt is in namespace +QT_USE_NAMESPACE + +#endif // QSERVICEFRAMEWORK_H + diff --git a/src/serviceframework/qserviceinterfacedescriptor.cpp b/src/serviceframework/qserviceinterfacedescriptor.cpp new file mode 100644 index 00000000..377c57aa --- /dev/null +++ b/src/serviceframework/qserviceinterfacedescriptor.cpp @@ -0,0 +1,418 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserviceinterfacedescriptor_p.h" +#ifndef QT_NO_DATASTREAM +#include <qdatastream.h> +#endif + +#include <QDebug> +#include <QStringList> + +QTM_BEGIN_NAMESPACE + +/*! + \class QServiceInterfaceDescriptor + \ingroup servicefw + \inmodule QtServiceFramework + \brief The QServiceInterfaceDescriptor class identifies a service implementation. + \since 1.0 + + A service can implement multiple interfaces and each interface can have multiple implementations. + The QServiceInterfaceDescriptor class enscapsulates this information, as illustrated + by the diagram below. + + \image qserviceinterfacedescriptor.png Service-Interface-Implementation + + The major version tag indicates the interface version and the minor version tag identifies the implementation + version. Subsequent versions of the same interface must be binary compatible to previous versions + of the same interface. + + In the above example service A and B implement the interface \i com.nokia.qt.x. + In fact Service A provides two different implementations for the very same interface. + This is indicated by the changed minor version number. Although Service B is + using the same interface it's implementation actually utilizes the second version of + the interface \i com.nokia.qt.x. Binary compatibility guarantees that clients + who know version 1 can utilize version 2. If an existing interface has to be changed + in a non-compatible way a new interface (name) is required. + + \section1 Namespaces + + A QServiceInterfaceDescriptor (the quadruble of service name, + interface name, interface version and implementation version) uniquely + identifies a service implementation on a device. Interface names follow + the java namespace convention. + + The namespace \i com.nokia.qt.* is reserved for future Qt development. + + \sa QServiceFilter, QServiceManager +*/ + +/*! + \enum QServiceInterfaceDescriptor::Attribute + + This enum describes the possible attribute types which can be attached + to a QServiceInterfaceDescriptor. + + \value Capabilities The capabilities attribute is a QStringList and + describes the capabilities that a service client + would require to use the service if capability + checks are enforced. + \value Location This attribute points to either the location + where the plug-in providing this service is stored or + where the name of the service IPC path is found. + If the service is plug-in based the location is the + name and/or path of the plugin. If the service is + IPC based the location is the name of the socket address. + \value ServiceDescription This attribute provides a general description for + the service. + \value InterfaceDescription This attribute provides a description for the interface + implementation. + \value ServiceType This attribute specifies the QService::Type that the + service is being provided. +*/ + +/*! + Creates a new QServiceInterfaceDescriptor. +*/ +QServiceInterfaceDescriptor::QServiceInterfaceDescriptor() + : d(0) +{ +} + +/*! + Destroys the QServiceInterfaceDescriptor object. +*/ +QServiceInterfaceDescriptor::~QServiceInterfaceDescriptor() +{ + if (d) + delete d; +} + +/*! + Creates a copy of QServiceInterfaceDescriptor contained in \a other. + \since 1.0 +*/ +QServiceInterfaceDescriptor::QServiceInterfaceDescriptor(const QServiceInterfaceDescriptor& other) + : d(0) +{ + (*this) = other; //use assignment operator +} + +/*! + \fn QServiceInterfaceDescriptor& QServiceInterfaceDescriptor::operator=(const QServiceInterfaceDescriptor& other) + + Copies the content of the QServiceInterfaceDescriptor object contained + in \a other into this one. + \since 1.0 +*/ +QServiceInterfaceDescriptor& QServiceInterfaceDescriptor::operator=(const QServiceInterfaceDescriptor& other) +{ + if ( !other.isValid() ) { + if (d) + delete d; + d = 0; + return *this; + } + + if (!d) + d = new QServiceInterfaceDescriptorPrivate; + + (*d) = *(other.d); + return *this; +} + +/*! + \fn bool QServiceInterfaceDescriptor::operator==(const QServiceInterfaceDescriptor& other) const + + Compares a QServiceInterfaceDescriptor to \a other. Returns true if they + are equal and false otherwise. + \since 1.0 +*/ +bool QServiceInterfaceDescriptor::operator==(const QServiceInterfaceDescriptor& other) const +{ + if (isValid() ^ other.isValid()) + return false; + + if (!d) + return true; + + if ((*d) == *(other.d)) + return true; + return false; +} + +/*! + \fn bool QServiceInterfaceDescriptor::operator!=(const QServiceInterfaceDescriptor& other) const + + Compares a QServiceInterfaceDescriptor to \a other. Returns true + if they are not equal and false otherwise. + \since 1.0 +*/ + +/*! + \fn bool QServiceInterfaceDescriptor::isValid() const + + Returns true if this descriptor is valid; otherwise returns false. + \since 1.0 +*/ +bool QServiceInterfaceDescriptor::isValid() const +{ + return d ? true : false; +} + +/*! + \fn bool QServiceInterfaceDescriptor::scope() const + + Returns true if this implementation is provided for all users on the system. + + \sa QService::Scope + \since 1.0 +*/ +QService::Scope QServiceInterfaceDescriptor::scope() const +{ + return d ? d->scope : QService::UserScope; +} + +/*! + \fn QString QServiceInterfaceDescriptor::serviceName() const + + Returns the name of service that provides this implementation. + \since 1.0 +*/ +QString QServiceInterfaceDescriptor::serviceName() const +{ + return d ? d->serviceName : QString(); +} + +/*! + \fn QString QServiceInterfaceDescriptor::interfaceName() const + + Returns the name of the interface that is implemented. + \since 1.0 +*/ +QString QServiceInterfaceDescriptor::interfaceName() const +{ + return d ? d->interfaceName : QString(); +} + +/*! + \fn int QServiceInterfaceDescriptor::majorVersion() const + + Returns the version of the interface. + + Subsequent versions of an interface are binary compatible + to previous versions of the same interface. If an interface + is broken it must use a new interface name. + \since 1.0 +*/ +int QServiceInterfaceDescriptor::majorVersion() const +{ + return d ? d->major : -1; +} + +/*! + \fn int QServiceInterfaceDescriptor::minorVersion() const + + Returns the version of the implementation. + \since 1.0 +*/ +int QServiceInterfaceDescriptor::minorVersion() const +{ + return d ? d->minor : -1; +} + +/*! + \fn QVariant QServiceInterfaceDescriptor::attribute(QServiceInterfaceDescriptor::Attribute which) const + + Returns the value for the attribute \a which; otherwise returns + an invalid QVariant. + \since 1.0 +*/ +QVariant QServiceInterfaceDescriptor::attribute(QServiceInterfaceDescriptor::Attribute which) const +{ + if (d) + return d->attributes.value(which); + return QVariant(); +} + +/*! + \fn QString QServiceInterfaceDescriptor::customAttribute(const QString& which) const + + Returns the value for the custom attribute \a which; otherwise + returns a null string. + \since 1.0 +*/ +QString QServiceInterfaceDescriptor::customAttribute(const QString& which) const +{ + if (d) + return d->customAttributes[which]; + return QString(); +} + +/*! + Returns a list of custom attributes attached to the service. + \since 1.0 + */ +QStringList QServiceInterfaceDescriptor::customAttributes() const +{ + if (d) + return d->customAttributes.keys(); + return QStringList(); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QServiceInterfaceDescriptor &desc) +{ + if (desc.isValid()) { + QString interface = QString(QLatin1String("%1 %2.%3")).arg(desc.interfaceName()) + .arg(desc.majorVersion() < 0 ? '?' : desc.majorVersion()) + .arg(desc.minorVersion() < 0 ? '?' : desc.minorVersion()); + dbg.nospace() << "QServiceInterfaceDescriptor("; + dbg.nospace() << "service=" << desc.serviceName() << ", "; + dbg.nospace() << "interface=" << interface; + dbg.nospace() << ")"; + } else { + dbg.nospace() << "QServiceInterfaceDescriptor(invalid)"; + } + return dbg.space(); +} +#endif + +#ifndef QT_NO_DATASTREAM + +QDataStream &operator<<(QDataStream &out, const QServiceInterfaceDescriptor::Attribute &k) +{ + out << qint8(k); + return out; +} + +QDataStream &operator>>(QDataStream &in, QServiceInterfaceDescriptor::Attribute &k) +{ + quint8 key; + in >> key; + k = (QServiceInterfaceDescriptor::Attribute)key; + return in; +} +/*! + \fn QDataStream &operator<<(QDataStream &out, const QServiceInterfaceDescriptor &dc) + \relates QServiceInterfaceDescriptor + + Writes service interface descriptor \a dc to the stream \a out and returns a reference + to the stream. + \since 1.0 +*/ + +QDataStream &operator<<(QDataStream &out, const QServiceInterfaceDescriptor &dc) +{ + const quint32 magicNumber = 0x77AFAFA; + const quint16 majorVersion = 1; + const quint16 minorVersion = 0; + const qint8 valid = dc.isValid(); + out << magicNumber << majorVersion << minorVersion; + out << valid; + if (valid) { + out << dc.d->serviceName; + out << dc.d->interfaceName; + out << dc.d->major; + out << dc.d->minor; + out << dc.d->attributes; + out << dc.d->customAttributes; + out << (qint8)dc.d->scope; + } + return out; +} + +/*! + \fn QDataStream &operator>>(QDataStream &in, QServiceInterfaceDescriptor &dc) + \relates QServiceInterfaceDescriptor + + Reads a service interface descriptor into \a dc from the stream \a in and returns a + reference to the stream. + \since 1.0 +*/ +QDataStream &operator>>(QDataStream &in, QServiceInterfaceDescriptor &dc) +{ + const quint32 magicNumber = 0x77AFAFA; + quint32 storedMagicNumber; + in >> storedMagicNumber; + if (storedMagicNumber != magicNumber) { + qWarning() << "Datastream doesn't provide searialized QServiceInterfaceDescriptor"; + return in; + } + + const quint16 currentMajorVersion = 1; + quint16 majorVersion = 0; + quint16 minorVersion = 0; + + in >> majorVersion >> minorVersion; + if (majorVersion != currentMajorVersion) { + qWarning() << "Unknown serialization format for QServiceInterfaceDescriptor."; + return in; + } + //Allow all minor versions. + + qint8 valid; + in >> valid; + if (valid) { + if (!dc.isValid()) + dc.d = new QServiceInterfaceDescriptorPrivate; + in >> dc.d->serviceName; + in >> dc.d->interfaceName; + in >> dc.d->major; + in >> dc.d->minor; + in >> dc.d->attributes; + in >> dc.d->customAttributes; + in >> valid; + dc.d->scope = (QService::Scope) valid; + } else { //input stream contains invalid descriptor + //use assignment operator + dc = QServiceInterfaceDescriptor(); + } + + return in; +} +#endif //QT_NO_DATASTREAM + + + +QTM_END_NAMESPACE + diff --git a/src/serviceframework/qserviceinterfacedescriptor.h b/src/serviceframework/qserviceinterfacedescriptor.h new file mode 100644 index 00000000..9d6614ce --- /dev/null +++ b/src/serviceframework/qserviceinterfacedescriptor.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICEINTERFACEDESCRIPTOR_H +#define QSERVICEINTERFACEDESCRIPTOR_H + +#include "qserviceframeworkglobal.h" +#include <QString> +#include <QVariant> +#include "qservice.h" + +QT_USE_NAMESPACE + +#ifdef SERVICE_XML_GENERATOR +#undef Q_SERVICEFW_EXPORT +#define Q_SERVICEFW_EXPORT +#endif + +QT_BEGIN_NAMESPACE +class QDebug; +class QStringList; +class QDataStream; +QT_END_NAMESPACE + +QT_BEGIN_HEADER + +QTM_BEGIN_NAMESPACE + +class QServiceInterfaceDescriptorPrivate; +class Q_SERVICEFW_EXPORT QServiceInterfaceDescriptor +{ +public: + enum Attribute { + Capabilities = 0, + Location, + ServiceDescription, + InterfaceDescription, + ServiceType + }; + + QServiceInterfaceDescriptor(); + QServiceInterfaceDescriptor(const QServiceInterfaceDescriptor& other); + ~QServiceInterfaceDescriptor(); + + QServiceInterfaceDescriptor& operator=(const QServiceInterfaceDescriptor& other); + bool operator==(const QServiceInterfaceDescriptor& other) const; + inline bool operator!=(const QServiceInterfaceDescriptor& other) const + { return !operator==(other); } + + QString serviceName() const; + QString interfaceName() const; + int majorVersion() const; + int minorVersion() const; + + bool isValid() const; + + QService::Scope scope() const; + + QVariant attribute(QServiceInterfaceDescriptor::Attribute which) const; + QString customAttribute(const QString& which) const; + QStringList customAttributes() const; + +private: + QServiceInterfaceDescriptorPrivate* d; + + friend class QServiceInterfaceDescriptorPrivate; + friend class QServiceManager; + friend class ServiceDatabase; + friend class ServiceMetaData; + friend class DatabaseManager; +#ifndef QT_NO_DATASTREAM + friend Q_SERVICEFW_EXPORT QDataStream &operator<<(QDataStream &, const QServiceInterfaceDescriptor &); + friend Q_SERVICEFW_EXPORT QDataStream &operator>>(QDataStream &, QServiceInterfaceDescriptor &); +#endif +}; + +#ifndef QT_NO_DATASTREAM +Q_SERVICEFW_EXPORT QDataStream &operator<<(QDataStream &, const QServiceInterfaceDescriptor &); +Q_SERVICEFW_EXPORT QDataStream &operator>>(QDataStream &, QServiceInterfaceDescriptor &); +#endif +#ifndef QT_NO_DEBUG_STREAM +Q_SERVICEFW_EXPORT QDebug operator<<(QDebug, const QServiceInterfaceDescriptor &); +#endif + + +QTM_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/serviceframework/qserviceinterfacedescriptor_p.h b/src/serviceframework/qserviceinterfacedescriptor_p.h new file mode 100644 index 00000000..512572c6 --- /dev/null +++ b/src/serviceframework/qserviceinterfacedescriptor_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICEINTERFACEDESCRIPTOR_P_H +#define QSERVICEINTERFACEDESCRIPTOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qserviceinterfacedescriptor.h" + +#include <QString> +#include <QHash> + +QTM_BEGIN_NAMESPACE + +#define SERVICE_INITIALIZED_ATTR "INITIALIZED" + +class QServiceInterfaceDescriptorPrivate +{ +public: + QServiceInterfaceDescriptorPrivate() + { + major = -1; + minor = -1; + scope = QService::UserScope; + } + + bool operator==(const QServiceInterfaceDescriptorPrivate& other) const + { + if (major == other.major + && minor == other.minor + && interfaceName == other.interfaceName + && serviceName == other.serviceName + && attributes == other.attributes + && customAttributes == other.customAttributes + && scope == other.scope) + return true; + return false; + } + + QServiceInterfaceDescriptorPrivate& operator=(const QServiceInterfaceDescriptorPrivate& other) + { + serviceName = other.serviceName; + interfaceName = other.interfaceName; + minor = other.minor; + major = other.major; + attributes = other.attributes; + customAttributes = other.customAttributes; + scope = other.scope; + + return *this; + } + + static QServiceInterfaceDescriptorPrivate *getPrivate(QServiceInterfaceDescriptor *descriptor) + { + return descriptor->d; + } + + static const QServiceInterfaceDescriptorPrivate *getPrivate(const QServiceInterfaceDescriptor *descriptor) + { + return descriptor->d; + } + + static void setPrivate(QServiceInterfaceDescriptor *descriptor, QServiceInterfaceDescriptorPrivate *p) + { + descriptor->d = p; + } + + QString serviceName; + QString interfaceName; + QHash<QServiceInterfaceDescriptor::Attribute, QVariant> attributes; + QHash<QString, QString> customAttributes; + int major; + int minor; + QService::Scope scope; +}; +QTM_END_NAMESPACE + +#endif //QSERVICEINTERFACEDESCRIPTOR_P_H diff --git a/src/serviceframework/qservicemanager.cpp b/src/serviceframework/qservicemanager.cpp new file mode 100644 index 00000000..a4ce9212 --- /dev/null +++ b/src/serviceframework/qservicemanager.cpp @@ -0,0 +1,796 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qservicemanager.h" +#include "qserviceplugininterface.h" +#include "qabstractsecuritysession.h" +#include "qserviceinterfacedescriptor_p.h" +#include "qremoteserviceregister_p.h" +#include "qremoteserviceregisterentry_p.h" + +#ifdef Q_OS_SYMBIAN + #include "databasemanager_symbian_p.h" +#else + #include "databasemanager_p.h" +#endif + +#include <QObject> +#include <QPluginLoader> +#include <QFile> +#include <QCoreApplication> +#include <QDir> +#include <QSystemSemaphore> + +QTM_BEGIN_NAMESPACE + +static QString qservicemanager_resolveLibraryPath(const QString &libNameOrPath) +{ + if (QFile::exists(libNameOrPath)) + return libNameOrPath; + + // try to find plug-in via QLibrary + QStringList paths = QCoreApplication::libraryPaths(); +#ifdef QTM_PLUGIN_PATH + paths << QLatin1String(QTM_PLUGIN_PATH)+QLatin1String("/serviceframework"); +#endif + for (int i=0; i<paths.count(); i++) { + QString libPath = QDir::toNativeSeparators(paths[i]) + QDir::separator() + libNameOrPath; + +#ifdef Q_OS_SYMBIAN + QFileInfo fi(libPath); + if (fi.suffix() == QLatin1String("dll")) + libPath = fi.completeBaseName() + QLatin1String(".qtplugin"); + else + libPath += QLatin1String(".qtplugin"); + + QLibrary lib(libPath); + if (QFile::exists(libPath) && lib.load()) { + lib.unload(); + return libPath; + } +#else + QLibrary lib(libPath); + if (lib.load()) { + lib.unload(); + return lib.fileName(); + } +#endif + } + return QString(); +} + +class QServicePluginCleanup : public QObject +{ + Q_OBJECT +public: + QServicePluginCleanup(QPluginLoader *loader, QObject *parent = 0) + : QObject(parent), + m_loader(loader) + { + } + + ~QServicePluginCleanup() + { + if (m_loader) { + //m_loader->unload(); + delete m_loader; + } + } + + QPluginLoader *m_loader; +}; + +class QServiceManagerPrivate : public QObject +{ + Q_OBJECT +public: + QServiceManager *manager; + DatabaseManager *dbManager; + QService::Scope scope; + QServiceManager::Error error; + + QServiceManagerPrivate(QServiceManager *parent = 0) + : QObject(parent), + manager(parent), + dbManager(new DatabaseManager) + { + connect(dbManager, SIGNAL(serviceAdded(QString, DatabaseManager::DbScope)), + SLOT(serviceAdded(QString, DatabaseManager::DbScope))); + connect(dbManager, SIGNAL(serviceRemoved(QString, DatabaseManager::DbScope)), + SLOT(serviceRemoved(QString, DatabaseManager::DbScope))); + } + + ~QServiceManagerPrivate() + { + delete dbManager; + } + + void setError(QServiceManager::Error err) + { + error = err; + } + + void setError() + { + switch (dbManager->lastError().code()) { + case DBError::NoError: + error = QServiceManager::NoError; + break; + case DBError::DatabaseNotOpen: + case DBError::InvalidDatabaseConnection: + case DBError::CannotCreateDbDir: + case DBError::CannotOpenServiceDb: + case DBError::NoWritePermissions: + case DBError::InvalidDatabaseFile: + error = QServiceManager::StorageAccessError; + break; + case DBError::LocationAlreadyRegistered: + error = QServiceManager::ServiceAlreadyExists; + break; + case DBError::IfaceImplAlreadyRegistered: + error = QServiceManager::ImplementationAlreadyExists; + break; + case DBError::NotFound: + error = QServiceManager::ComponentNotFound; + break; + case DBError::InvalidDescriptorScope: + error = QServiceManager::InvalidServiceInterfaceDescriptor; + break; + case DBError::SqlError: + case DBError::IfaceIDNotExternal: + case DBError::ExternalIfaceIDFound: + case DBError::UnknownError: + error = QServiceManager::UnknownError; + break; + } + } + +private slots: + void serviceAdded(const QString &service, DatabaseManager::DbScope dbScope) + { + QService::Scope s = (dbScope == DatabaseManager::SystemScope ? + QService::SystemScope : QService::UserScope); + emit manager->serviceAdded(service, s); + } + + void serviceRemoved(const QString &service, DatabaseManager::DbScope dbScope) + { + QService::Scope s = (dbScope == DatabaseManager::SystemScope ? + QService::SystemScope : QService::UserScope); + emit manager->serviceRemoved(service, s); + } +}; + +/*! + \class QServiceManager + \ingroup servicefw + \inmodule QtServiceFramework + \brief The QServiceManager class enables the loading of service plugins + and the (de)registration of services. + \since 1.0 + + A service is a stand-alone component that can be used by multiple clients. + Each service implementation must derive from QObject. Clients request a + reference to a service via \l loadInterface() or \l loadLocalTypedInterface(). + + Services are separate deliveries in the form of plug-ins. New services can be (de)registered + at any time via \l addService() and \l removeService() respectively. Such an event is + published via the \l serviceAdded() and \l serviceRemoved() signal. + Each service plug-in must implement QServicePluginInterface. + + Each plug-in may support multiple interfaces and may even provide multiple implementations + for the same interface. Individual implementations are identified via + QServiceInterfaceDescriptor. For a more detailed explanation of services and how they relate to + interface and their implementations please see QServiceInterfaceDescriptor. + + \sa QServicePluginInterface, QServiceContext, QAbstractSecuritySession +*/ + +/*! + \enum QServiceManager::Error + Defines the possible errors for the service manager. + + \value NoError No error occurred. + \value StorageAccessError The service data storage is not accessible. This could be because the caller does not have the required permissions. + \value InvalidServiceLocation The service was not found at its specified \l{QServiceInterfaceDescriptor::Location}{location}. + \value InvalidServiceXml The XML defining the service metadata is invalid. + \value InvalidServiceInterfaceDescriptor The service interface descriptor is invalid, or refers to an interface implementation that cannot be accessed in the current scope. + \value ServiceAlreadyExists Another service has previously been registered with the same \l{QServiceInterfaceDescriptor::Location}{location}. + \value ImplementationAlreadyExists Another service that implements the same interface version has previously been registered. + \value PluginLoadingFailed The service plugin cannot be loaded. + \value ComponentNotFound The service or interface implementation has not been registered. + \value ServiceCapabilityDenied The security session does not permit service access based on its capabilities. + \value UnknownError An unknown error occurred. +*/ + +/*! + \fn void QServiceManager::serviceAdded(const QString& serviceName, QService::Scope scope) + + This signal is emited whenever a new service with the given + \a serviceName has been registered with the service manager. + \a scope indicates where the service was added. + + If the manager scope is QService::SystemScope, it will not receive + notifications about services added in the user scope. + + \sa addService() + \since 1.0 +*/ + +/*! + \fn void QServiceManager::serviceRemoved(const QString& serviceName, QService::Scope scope) + + This signal is emited whenever a service with the given + \a serviceName has been deregistered with the service manager. + \a scope indicates where the service was added. + + If the manager scope is QService::SystemScope, it will not receive + notifications about services removed in the user scope. + + \sa removeService() + \since 1.0 +*/ + +/*! + Creates a service manager with the given \a parent. + + The scope will default to QService::UserScope. +*/ +QServiceManager::QServiceManager(QObject *parent) + : QObject(parent), + d(new QServiceManagerPrivate(this)) +{ + qRegisterMetaType<QService::UnrecoverableIPCError>("QService::UnrecoverableIPCError"); + d->scope = QService::UserScope; +} + +/*! + Creates a service manager with the given \a scope and \a parent. + \since 1.0 +*/ +QServiceManager::QServiceManager(QService::Scope scope, QObject *parent) + : QObject(parent), + d(new QServiceManagerPrivate(this)) +{ + d->scope = scope; +} + +/*! + Destroys the service manager. +*/ +QServiceManager::~QServiceManager() +{ + delete d; +} + +/*! + Returns the scope used for registering and searching of services. + \since 1.0 +*/ +QService::Scope QServiceManager::scope() const +{ + return d->scope; +} + +/*! + Returns a list of the services that provide the interface specified by + \a interfaceName. If \a interfaceName is empty, this function returns + a list of all available services in this manager's scope. + \since 1.0 +*/ +QStringList QServiceManager::findServices(const QString& interfaceName) const +{ + d->setError(NoError); + QStringList services; + services = d->dbManager->getServiceNames(interfaceName, + d->scope == QService::SystemScope ? DatabaseManager::SystemScope : DatabaseManager::UserScope); + d->setError(); + return services; +} + +/*! + Returns a list of the interfaces that match the specified \a filter. + \since 1.0 +*/ +QList<QServiceInterfaceDescriptor> QServiceManager::findInterfaces(const QServiceFilter& filter) const +{ + d->setError(NoError); + QList<QServiceInterfaceDescriptor> descriptors = d->dbManager->getInterfaces(filter, + d->scope == QService::SystemScope ? DatabaseManager::SystemScope : DatabaseManager::UserScope); + if (descriptors.isEmpty() && d->dbManager->lastError().code() != DBError::NoError) { + d->setError(); + return QList<QServiceInterfaceDescriptor>(); + } + return descriptors; +} + +/*! + Returns a list of the interfaces provided by the service named + \a serviceName. If \a serviceName is empty, this function returns + a list of all available interfaces in this manager's scope. + \since 1.0 +*/ +QList<QServiceInterfaceDescriptor> QServiceManager::findInterfaces(const QString& serviceName) const +{ + QServiceFilter filter; + if (!serviceName.isEmpty()) + filter.setServiceName(serviceName); + return findInterfaces(filter); +} + +/*! + Loads and returns the interface specified by \a interfaceName, as + provided by the default service for this interface, using the given + \a context and \a session. \a context and \a session object are owned + by the caller of this function. + + The caller takes ownership of the returned pointer. + + This function returns a null pointer if the requested service cannot be found. + + The security session object is not mandatory. If the session pointer is null, + the service manager will not perform any checks. Therefore it is assumed that + the service manager client is trusted as it controls whether service capabilities + are enforced during service loading. + + \sa setInterfaceDefault(), interfaceDefault() + \since 1.0 +*/ +QObject* QServiceManager::loadInterface(const QString& interfaceName, QServiceContext* context, QAbstractSecuritySession* session) +{ + return loadInterface(interfaceDefault(interfaceName), context, session); +} + +/*! + Loads and returns the interface specified by \a descriptor using the + given \a context and \a session. \a context and \a session object are owned + by the caller of this function. + + The caller takes ownership of the returned pointer. + + This function returns a null pointer if the requested service cannot be found. + + The security session object is not mandatory. If the session pointer is null, + the service manager will not perform any checks. Therefore it is assumed that + the service manager client is trusted as it controls whether service capabilities + are enforced during service loading. + \since 1.0 +*/ +QObject* QServiceManager::loadInterface(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session) +{ + d->setError(NoError); + if (!descriptor.isValid()) { + d->setError(InvalidServiceInterfaceDescriptor); + return 0; + } + + const QStringList serviceCaps = descriptor.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList(); + if ( session && !session->isAllowed(serviceCaps) ) { + d->setError(ServiceCapabilityDenied); + return 0; + } + + const QString location = descriptor.attribute(QServiceInterfaceDescriptor::Location).toString(); + const bool isInterProcess = (descriptor.attribute(QServiceInterfaceDescriptor::ServiceType).toInt() + == QService::InterProcess); + if (isInterProcess) { + //ipc service + const int majorversion = descriptor.majorVersion(); + const int minorversion = descriptor.minorVersion(); + QString version = QString::number(majorversion) + "." + QString::number(minorversion); + + QRemoteServiceRegister::Entry serviceEntry; + serviceEntry.d->iface = descriptor.interfaceName(); + serviceEntry.d->service = descriptor.serviceName(); + serviceEntry.d->ifaceVersion = version; + QObject* service = QRemoteServiceRegisterPrivate::proxyForService(serviceEntry, location); + if (!service) + d->setError(InvalidServiceLocation); + + //client owns proxy object + return service; + } + + const QString serviceFilePath = qservicemanager_resolveLibraryPath(location); + if (serviceFilePath.isEmpty()) { + d->setError(InvalidServiceLocation); + return 0; + } + + QPluginLoader *loader = new QPluginLoader(serviceFilePath); + //pluginIFace is same for all service instances of the same plugin + //calling loader->unload deletes pluginIFace automatically if now other + //service instance is around + QServicePluginInterface *pluginIFace = qobject_cast<QServicePluginInterface *>(loader->instance()); + if (pluginIFace) { + + //check initialization first as the service may be a pre-registered one + bool doLoading = true; + QString serviceInitialized = descriptor.customAttribute(SERVICE_INITIALIZED_ATTR); + if (!serviceInitialized.isEmpty() && (serviceInitialized == QLatin1String("NO"))) { + // open/create the semaphore using the service's name as identifier + QSystemSemaphore semaphore(descriptor.serviceName(), 1); + if (semaphore.error() != QSystemSemaphore::NoError) { + //try to create it + semaphore.setKey(descriptor.serviceName(), 1, QSystemSemaphore::Create); + } + if (semaphore.error() == QSystemSemaphore::NoError && semaphore.acquire()) { + pluginIFace->installService(); + DatabaseManager::DbScope scope = d->scope == QService::UserScope ? + DatabaseManager::UserOnlyScope : DatabaseManager::SystemScope; + d->dbManager->serviceInitialized(descriptor.serviceName(), scope); + // release semaphore + semaphore.release(); + } + else + doLoading = false; + } + + if (doLoading) { + QObject *obj = pluginIFace->createInstance(descriptor, context, session); + if (obj) { + QServicePluginCleanup *cleanup = new QServicePluginCleanup(loader); + QObject::connect(obj, SIGNAL(destroyed()), cleanup, SLOT(deleteLater())); + return obj; + } + } + } + + //loader->unload(); + delete loader; + d->setError(PluginLoadingFailed); + + return 0; +} + +/*! + \fn T* QServiceManager::loadLocalTypedInterface(const QString& interfaceName, QServiceContext* context, QAbstractSecuritySession* session) + + Loads the service object implementing \a interfaceName, + as provided by the default service for this interface, using the given + \a context and \a session. \a context and \a session object are owned + by the caller of this function. The template class must be derived from QObject. + + If \a interfaceName is not a known interface the returned pointer will be null. + + Note that using this function implies that service and client share + the implementation of T which means that service and client become tightly coupled. + This may cause issue during later updates as certain changes may require code changes + to the service and client. + + The caller takes ownership of the returned pointer. + + The security session object is not mandatory. If the session pointer is null, + the service manager will not perform any checks. Therefore it is assumed that + the service manager client is trusted as it controls whether service capabilities + are enforced during service loading. + + \sa setInterfaceDefault(), interfaceDefault() + \since 1.0 +*/ + + +/*! + \fn T* QServiceManager::loadLocalTypedInterface(const QServiceInterfaceDescriptor& serviceDescriptor, QServiceContext* context, QAbstractSecuritySession* session) + + Loads the service object identified by \a serviceDescriptor + using the given \a context and \a session. \a context and \a session object are owned + by the caller of this function. The template class must be derived from QObject. + + If the \a serviceDescriptor is not valid the returned pointer will be null. + + Note that using this function implies that service and client share + the implementation of T which means that service and client become tightly coupled. + This may cause issue during later updates as certain changes may require code changes + to the service and client. + + The caller takes ownership of the returned pointer. + + The security session object is not mandatory. If the session pointer is null, + the service manager will not perform any checks. Therefore it is assumed that + the service manager client is trusted as it controls whether service capabilities + are enforced during service loading. + \since 1.0 +*/ + +/*! + Registers the service defined by the XML file at \a xmlFilePath. + Returns true if the registration succeeded, and false otherwise. + + If a previously unkown interface is added the newly registered service automatically + becomes the new default service provider for the new interface. + + A service plugin cannot be added if another service is already registered + with the same plugin file path. A service plugin also cannot be added if + the service is already registered and implements any of the same interface + versions that the new plugin implements. + + \sa removeService(), setInterfaceDefault() + \since 1.0 +*/ +bool QServiceManager::addService(const QString& xmlFilePath) +{ + QFile *f = new QFile(xmlFilePath); + bool b = addService(f); + delete f; + return b; +} + +/*! + Registers the service defined by the XML data from the given \a device. + Returns true if the registration succeeded, and false otherwise. If a + previously unkown interface is added the newly registered service + automatically becomes the new default service provider for the new + interface. + + Registering a service also causes QServicePluginInterface::installService() + to be called on the service. If the service plugin is not accessible + (e.g. if the plugin file is not found) and \c installService() cannot + be invoked on the service, the registration fails and this method returns + false. + + A service plugin cannot be added if another service is already registered + with the same plugin file path. A service plugin also cannot be added if + the service is already registered and implements any of the same interface + versions that the new plugin implements. + + Services are always added based on the \l scope() of the current + service manager instance. + + \sa removeService(), setInterfaceDefault() + \since 1.0 +*/ +bool QServiceManager::addService(QIODevice *device) +{ + d->setError(NoError); + ServiceMetaData parser(device); + if (!parser.extractMetadata()) { + d->setError(InvalidServiceXml); + return false; + } + const ServiceMetaDataResults data = parser.parseResults(); + + DatabaseManager::DbScope scope = d->scope == QService::UserScope ? + DatabaseManager::UserOnlyScope : DatabaseManager::SystemScope; + ServiceMetaDataResults results = parser.parseResults(); + + bool result = d->dbManager->registerService(results, scope); + + if (results.type == QService::InterProcess) + return result; + + //test the new plug-in + if (result) { + QPluginLoader *loader = new QPluginLoader(qservicemanager_resolveLibraryPath(data.location)); + QServicePluginInterface *pluginIFace = qobject_cast<QServicePluginInterface *>(loader->instance()); + if (pluginIFace) { + pluginIFace->installService(); + } else { + d->setError(PluginLoadingFailed); + result = false; + d->dbManager->unregisterService(data.name, scope); + } + //loader->unload(); + delete loader; + } else { + d->setError(); + } + + return result; +} + +/*! + Unregisters the service specified by \a serviceName. + + Returns true if the unregistration succeeded, and false otherwise. + + If a default service implementation is removed and there are other implementations + for the same interface, the service manager chooses the implementation with the + highest version number as the new default. If there is more than one serivce + with the same version number, the service manager makes a random choice with + regards to the new default implementation. If this is + not the desired behaviour the default selection should be updated + via setInterfaceDefault(). + + Services are always removed based on the \l scope() of the current + service manager instance. + + \sa addService() + \since 1.0 +*/ +bool QServiceManager::removeService(const QString& serviceName) +{ + d->setError(NoError); + if (serviceName.isEmpty()) { + d->setError(ComponentNotFound); + return false; + } + + // Call QServicePluginInterface::uninstallService() on all plugins that + // match this service + + QSet<QString> pluginPathsSet; + QList<QServiceInterfaceDescriptor> descriptors = findInterfaces(serviceName); + for (int i=0; i<descriptors.count(); i++) { + const QString loc = descriptors[i].attribute(QServiceInterfaceDescriptor::Location).toString(); + const int type = descriptors[i].attribute(QServiceInterfaceDescriptor::ServiceType).toInt(); + //exclude ipc services + if (type <= QService::Plugin) + pluginPathsSet << loc; + } + + QList<QString> pluginPaths = pluginPathsSet.toList(); + for (int i=0; i<pluginPaths.count(); i++) { + QPluginLoader *loader = new QPluginLoader(qservicemanager_resolveLibraryPath(pluginPaths[i])); + QServicePluginInterface *pluginIFace = qobject_cast<QServicePluginInterface *>(loader->instance()); + if (pluginIFace) + pluginIFace->uninstallService(); + else + qWarning() << "QServiceManager: unable to invoke uninstallService() on removed service"; + //loader->unload(); + delete loader; + } + + if (!d->dbManager->unregisterService(serviceName, d->scope == QService::UserScope ? + DatabaseManager::UserOnlyScope : DatabaseManager::SystemScope)) { + d->setError(); + return false; + } + return true; +} + +/*! + Sets the default interface implementation for \a interfaceName to the + matching interface implementation provided by \a service. + + If \a service provides more than one interface implementation for + \a interfaceName, the newest version of the interface is set as the + default. + + Returns true if the operation succeeded, and false otherwise. + + \bold {Note:} When in system scope, the \a service must be a system-wide + service rather than a user-specific service; otherwise, this will fail. + \since 1.0 +*/ +bool QServiceManager::setInterfaceDefault(const QString &service, const QString &interfaceName) +{ + d->setError(NoError); + if (service.isEmpty() || interfaceName.isEmpty()) { + d->setError(ComponentNotFound); + return false; + } + DatabaseManager::DbScope scope = d->scope == QService::SystemScope ? + DatabaseManager::SystemScope : DatabaseManager::UserScope; + if (!d->dbManager->setInterfaceDefault(service, interfaceName, scope)) { + d->setError(); + return false; + } + return true; +} + +/*! + \overload + + Sets the interface implementation specified by \a descriptor to be the + default implementation for the particular interface specified in the + descriptor. + + Returns true if the operation succeeded, and false otherwise. + + \bold {Note:} When in system scope, the \a descriptor must refer to a + system-wide service rather than a user-specific service; otherwise, this + will fail. + \since 1.0 +*/ +bool QServiceManager::setInterfaceDefault(const QServiceInterfaceDescriptor& descriptor) +{ + d->setError(NoError); + DatabaseManager::DbScope scope = d->scope == QService::SystemScope ? + DatabaseManager::SystemScope : DatabaseManager::UserScope; + if (!d->dbManager->setInterfaceDefault(descriptor, scope)) { + d->setError(); + return false; + } + return true; +} + +/*! + Returns the default interface implementation for the given \a interfaceName. + \since 1.0 +*/ +QServiceInterfaceDescriptor QServiceManager::interfaceDefault(const QString& interfaceName) const +{ + d->setError(NoError); + DatabaseManager::DbScope scope = d->scope == QService::SystemScope ? + DatabaseManager::SystemScope : DatabaseManager::UserScope; + QServiceInterfaceDescriptor info = d->dbManager->interfaceDefault(interfaceName, scope); + if (d->dbManager->lastError().code() != DBError::NoError) { + d->setError(); + return QServiceInterfaceDescriptor(); + } + return info; +} + +/*! + Returns the type of error that last occurred. + \since 1.0 +*/ +QServiceManager::Error QServiceManager::error() const +{ + return d->error; +} + +/*! + \internal + \since 1.0 +*/ +void QServiceManager::connectNotify(const char *signal) +{ + if (QLatin1String(signal) == SIGNAL(serviceAdded(QString,QService::Scope)) + || QLatin1String(signal) == SIGNAL(serviceRemoved(QString,QService::Scope))) { + if (d->scope != QService::SystemScope) + d->dbManager->setChangeNotificationsEnabled(DatabaseManager::UserScope, true); + d->dbManager->setChangeNotificationsEnabled(DatabaseManager::SystemScope, true); + } +} + +/*! + \internal + \since 1.0 +*/ +void QServiceManager::disconnectNotify(const char *signal) +{ + if (QLatin1String(signal) == SIGNAL(serviceAdded(QString,QService::Scope)) + || QLatin1String(signal) == SIGNAL(serviceRemoved(QString,QService::Scope))) { + if (receivers(SIGNAL(serviceAdded(QString,QService::Scope))) == 0 + && receivers(SIGNAL(serviceRemoved(QString,QService::Scope))) == 0) { + if (d->scope != QService::SystemScope) + d->dbManager->setChangeNotificationsEnabled(DatabaseManager::UserScope, false); + d->dbManager->setChangeNotificationsEnabled(DatabaseManager::SystemScope, false); + } + } +} + +#include "moc_qservicemanager.cpp" +#include "qservicemanager.moc" +QTM_END_NAMESPACE + diff --git a/src/serviceframework/qservicemanager.h b/src/serviceframework/qservicemanager.h new file mode 100644 index 00000000..af14eca6 --- /dev/null +++ b/src/serviceframework/qservicemanager.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICEMANAGER_H +#define QSERVICEMANAGER_H + +#include "qserviceframeworkglobal.h" + +#include "qservice.h" +#include "qserviceinterfacedescriptor.h" +#include "qservicefilter.h" + +#include <QObject> +#include <QList> +#include <QStringList> +#include <QDebug> + + +QT_BEGIN_HEADER + +QTM_BEGIN_NAMESPACE + +class QServiceContext; +class QAbstractSecuritySession; +class QServiceFilter; +class QServiceManagerPrivate; +class Q_SERVICEFW_EXPORT QServiceManager : public QObject +{ + Q_OBJECT +public: + + enum Error { + NoError, + StorageAccessError, + InvalidServiceLocation, + InvalidServiceXml, + InvalidServiceInterfaceDescriptor, + ServiceAlreadyExists, + ImplementationAlreadyExists, + PluginLoadingFailed, + ComponentNotFound, + ServiceCapabilityDenied, + UnknownError = 100 + }; + + explicit QServiceManager(QObject *parent = 0); + explicit QServiceManager(QService::Scope scope, QObject *parent = 0); + ~QServiceManager(); + + QService::Scope scope() const; + + QStringList findServices(const QString& interfaceName = QString()) const; + QList<QServiceInterfaceDescriptor> findInterfaces(const QServiceFilter& filter = QServiceFilter()) const; + QList<QServiceInterfaceDescriptor> findInterfaces(const QString& serviceName) const; + + QObject* loadInterface(const QString& interfaceName, QServiceContext* context = 0, QAbstractSecuritySession* session = 0); + QObject* loadInterface(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context = 0, QAbstractSecuritySession* session = 0); + + template <class T> + T* loadLocalTypedInterface(const QString& interfaceName, QServiceContext* context = 0, QAbstractSecuritySession* session = 0) + { + return loadLocalTypedInterface<T>(interfaceDefault(interfaceName), context, session); + } + + template <class T> + T* loadLocalTypedInterface(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context = 0, QAbstractSecuritySession* session = 0) + { + T* instance = 0; + if (descriptor.isValid()) { + QObject* obj = loadInterface(descriptor, context, session); + if (!obj) return 0; + + //TODO this should really be + //instance = qobject_cast<T *>(loadInterface(descriptor, context, session)); + //check why qobject_cast fails + const char* templateClassName = reinterpret_cast<T *>(0)->staticMetaObject.className(); + const QMetaObject* source = obj->metaObject(); + do { + if (strcmp(templateClassName,source->className())==0) { + instance = static_cast<T *>(obj); + break; + } + source = source->superClass(); + } while (source != 0); + if (!instance) + delete obj; + } + return instance; + } + + bool addService(const QString& xmlFilePath); + bool addService(QIODevice* xmlDevice); + bool removeService(const QString& serviceName); + + bool setInterfaceDefault(const QString &service, const QString &interfaceName); + bool setInterfaceDefault(const QServiceInterfaceDescriptor& descriptor); + + QServiceInterfaceDescriptor interfaceDefault(const QString& interfaceName) const; + + Error error() const; + +protected: + void connectNotify(const char *signal); + void disconnectNotify(const char *signal); + +Q_SIGNALS: + void serviceAdded(const QString& serviceName, QService::Scope scope); + void serviceRemoved(const QString& serviceName, QService::Scope scope); + +private: + friend class QServiceManagerPrivate; + QServiceManagerPrivate* d; +}; + + +QTM_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/serviceframework/qserviceplugininterface.cpp b/src/serviceframework/qserviceplugininterface.cpp new file mode 100644 index 00000000..e5b4d60a --- /dev/null +++ b/src/serviceframework/qserviceplugininterface.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserviceplugininterface.h" + +QTM_BEGIN_NAMESPACE +/*! + \class QServicePluginInterface + \ingroup servicefw + \inmodule QtServiceFramework + \brief The QServicePluginInterface class defines the interface + that every plug-in based service must implement. + \since 1.0 +*/ + +/*! + \internal + \since 1.0 +*/ +QServicePluginInterface::QServicePluginInterface() +{ +} + +/*! + \internal + \since 1.0 +*/ +QServicePluginInterface::~QServicePluginInterface() +{ +} + +/*! + \fn QObject* QServicePluginInterface::createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, + QAbstractSecuritySession* securitySession) + + Creates a new instance of the service specified by \a descriptor. The service + may use the given \a context and \a securitySession. \a context and \a securitySession object are owned + by the client of the service. + + This function returns a null pointer if the plug-in doesn't + support the given \a descriptor. + \since 1.0 +*/ + +/*! + \fn bool QServicePluginInterface::installService() + + This function is called by QServiceManager as part of the service registration process. It can be + used to initialize the environment or the creation of external settings files which may be required + during the execution of the service. + + The default implementation for this function does nothing. + + \sa QServiceManager::addService() + \since 1.0 +*/ +void QServicePluginInterface::installService() +{ +} + +/*! + \fn bool QServicePluginInterface::uninstallService() + + This function is called bu QServiceManager as part of the deregistration process for services. This + gives the service the possibility to perform cleanup operations such as the removal of setting files + on the hard drive. + + The default implementation for this function does nothing. + + \sa QServiceManager::removeService() + \since 1.0 +*/ + +void QServicePluginInterface::uninstallService() +{ +} + +QTM_END_NAMESPACE diff --git a/src/serviceframework/qserviceplugininterface.h b/src/serviceframework/qserviceplugininterface.h new file mode 100644 index 00000000..0c765b5d --- /dev/null +++ b/src/serviceframework/qserviceplugininterface.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICEPLUGININTERFACE_H +#define QSERVICEPLUGININTERFACE_H + +#include "qserviceframeworkglobal.h" +#include <QtPlugin> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + + +class QServiceInterfaceDescriptor; +class QServiceContext; +class QAbstractSecuritySession; + +class Q_SERVICEFW_EXPORT QServicePluginInterface +{ +public: + QServicePluginInterface(); + virtual ~QServicePluginInterface(); + + virtual QObject* createInstance(const QServiceInterfaceDescriptor& descriptor, + QServiceContext* context, + QAbstractSecuritySession* session) = 0; + + virtual void installService(); + virtual void uninstallService(); +}; + +//moc doesn't understand QTM_PREPEND_NAMESPACE() macro. we have to be explicit +//Q_DECLARE_INTERFACE(QTM_PREPEND_NAMESPACE(QServicePluginInterface), "com.nokia.qt.QServicePluginInterface/1.0") +Q_DECLARE_INTERFACE(QServicePluginInterface, "com.nokia.qt.QServicePluginInterface/1.0") +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/serviceframework/servicedatabase.cpp b/src/serviceframework/servicedatabase.cpp new file mode 100644 index 00000000..d564701f --- /dev/null +++ b/src/serviceframework/servicedatabase.cpp @@ -0,0 +1,2328 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define QT_SFW_SERVICEDATABASE_DEBUG + +#include "servicedatabase_p.h" +#include <QDir> +#include <QSet> +#include "qserviceinterfacedescriptor.h" +#include "qserviceinterfacedescriptor_p.h" +#include <QUuid> +#include "dberror_p.h" + +//database name +#define RESOLVERDATABASE "services.db" + +//database table names +#define SERVICE_TABLE "Service" +#define INTERFACE_TABLE "Interface" +#define DEFAULTS_TABLE "Defaults" +#define SERVICE_PROPERTY_TABLE "ServiceProperty" +#define INTERFACE_PROPERTY_TABLE "InterfaceProperty" + +//separator +#define RESOLVERDATABASE_PATH_SEPARATOR "//" + +#ifdef QT_SFW_SERVICEDATABASE_DEBUG +#include <QDebug> +#endif + +#define SERVICE_DESCRIPTION_KEY "DESCRIPTION" +#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN +#define SECURITY_TOKEN_KEY "SECURITYTOKEN" +#endif +#define INTERFACE_DESCRIPTION_KEY "DESCRIPTION" +#define SERVICE_INITIALIZED_KEY SERVICE_INITIALIZED_ATTR +#define INTERFACE_CAPABILITY_KEY "CAPABILITIES" +#define INTERFACE_SERVICETYPE_KEY "SERVICETYPE" + +//service prefixes +#define SERVICE_IPC_PREFIX "_q_ipc_addr:" + +QTM_BEGIN_NAMESPACE + +enum TBindIndexes + { + EBindIndex=0, + EBindIndex1, + EBindIndex2, + EBindIndex3, + EBindIndex4, + EBindIndex5, + EBindIndex6, + EBindIndex7 + }; + + +/* + \class ServiceDatabase + The ServiceDatabase is responsible for the management of a single + service database. It provides operations for: + - opening and closing a connection with the database, + - registering and unregistering services + - querying for services and interfaces + - setting and getting default interface implementations. +*/ + +/* + Constructor +*/ +ServiceDatabase::ServiceDatabase(void) +:m_isDatabaseOpen(false),m_inTransaction(false) +{ +} + +/* + Destructor +*/ +ServiceDatabase::~ServiceDatabase() +{ + close(); +} + +/* + Opens the service database + The method creates or opens database and creates tables if they are not present + Returns true if the operation was successful, false if not. +*/ +bool ServiceDatabase::open() +{ + if (m_isDatabaseOpen) + return true; + + QString path; + + //Create full path to database + if (m_databasePath.isEmpty ()) + m_databasePath = databasePath(); + + path = m_databasePath; + QFileInfo dbFileInfo(path); + if (!dbFileInfo.dir().exists()) { + // Create the path with QFile, avoids problems with S60 3.2/3.1 + // Avoid security violation with PlatSec +#ifndef Q_OS_SYMBIAN + QDir::root().mkpath(dbFileInfo.path()); +#endif + QFile file(path); + if (!file.open(QIODevice::ReadWrite)) { + QString errorText(QLatin1String("Could not create database directory: %1")); + m_lastError.setError(DBError::CannotCreateDbDir, errorText.arg(dbFileInfo.path())); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::open():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + close(); + return false; + } + file.close(); + } + + m_connectionName = dbFileInfo.completeBaseName(); + QSqlDatabase database; + if (QSqlDatabase::contains(m_connectionName)) { + database = QSqlDatabase::database(m_connectionName); + } else { + database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), m_connectionName); + database.setDatabaseName(path); + } + + if (!database.isValid()){ + m_lastError.setError(DBError::InvalidDatabaseConnection); + close(); + return false; + } + + //Create or open database + if (!database.isOpen()) { + if (!database.open()) { + m_lastError.setError(DBError::SqlError, database.lastError().text()); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::open():-" + << "Problem:" << "Could not open database. " + << "Reason:" << m_lastError.text(); +#endif + close(); + return false; + } + } + m_isDatabaseOpen = true; + + //Check database structure (tables) and recreate tables if neccessary + //If one of tables is missing remove all tables and recreate them + //This operation is required in order to avoid data coruption + if (!checkTables()) { + if (dropTables()) { + if (createTables()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qDebug() << "ServiceDatabase::open():-" + << "Database tables recreated"; +#endif + } else { + //createTable() should've handled error message + //and warning + close(); + return false; + } + } + else { + //dropTables() should've handled error message + //and warning + close(); + return false; + } + } + return true; +} + +/* + Adds a \a service into the database. + + May set the following error codes + DBError::NoError + DBError::LocationAlreadyRegistered + DBError::IfaceImplAlreadyRegistered + DBError::SqlError + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +//bool ServiceDatabase::registerService(ServiceMetaData &service) +bool ServiceDatabase::registerService(const ServiceMetaDataResults &service, const QString &securityToken) +{ + // Derive the location name with the service type prefix to be stored + QString locationPrefix = service.location; + int type = service.interfaces[0].d->attributes[QServiceInterfaceDescriptor::ServiceType].toInt(); + if (type == QService::InterProcess) + locationPrefix = SERVICE_IPC_PREFIX + service.location; + +#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + Q_UNUSED(securityToken); +#else + if (securityToken.isEmpty()) { + QString errorText("Access denied, no security token provided (for registering service: \"%1\")"); + m_lastError.setError(DBError::NoWritePermissions, errorText.arg(service.name)); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << "Problem: Unable to register service," + << "reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } +#endif + + if (!checkConnection()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + if (!beginTransaction(&query, Write)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << "Unable to begin transaction," + << "reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + //See if the service's location has already been previously registered + QString statement(QLatin1String("SELECT Name from Service WHERE Location=? COLLATE NOCASE")); + QList<QVariant> bindValues; + bindValues.append(locationPrefix); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + if (query.next()) { + QString alreadyRegisteredService = query.value(EBindIndex).toString(); + const QString errorText = QLatin1String("Cannot register service \"%1\". Service location \"%2\" is already " + "registered to service \"%3\". \"%3\" must first be deregistered " + "for new registration to take place."); + + m_lastError.setError(DBError::LocationAlreadyRegistered, + errorText.arg(service.name) + .arg(service.location) + .arg(alreadyRegisteredService)); + + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return false; + } + +#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + // If service(s) have already been registered with same name, they must all come from + // same application. Fetch a service with given name and if such exists, check that its + // security ID equals to the security ID of the current registrar. + // One application may register multiple services with same name (different location), + // hence the keyword DISTINCT. + statement = "SELECT DISTINCT ServiceProperty.Value FROM Service, ServiceProperty " + "WHERE Service.ID = ServiceProperty.ServiceID " + "AND ServiceProperty.Key = ? " + "AND Service.Name = ?"; + bindValues.clear(); + bindValues.append(SECURITY_TOKEN_KEY); + bindValues.append(service.name); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + QString existingSecurityToken; + if (query.next()) { + existingSecurityToken = query.value(EBindIndex).toString(); + } + if (!existingSecurityToken.isEmpty() && (existingSecurityToken != securityToken)) { + QString errorText("Access denied: \"%1\""); + m_lastError.setError(DBError::NoWritePermissions, errorText.arg(service.name)); + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << "Problem: Unable to register service," + << "reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } +#endif // QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + + // Checks done, create new rows into tables. + statement = QLatin1String("INSERT INTO Service(ID,Name,Location) VALUES(?,?,?)"); + + qsrand(QTime::currentTime().msec()); + QString serviceID = QUuid::createUuid().toString(); + + bindValues.clear(); + bindValues.append(serviceID); + bindValues.append(service.name); + bindValues.append(locationPrefix); + + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + statement = QLatin1String("INSERT INTO ServiceProperty(ServiceID,Key,Value) VALUES(?,?,?)"); + bindValues.clear(); + bindValues.append(serviceID); + bindValues.append(QLatin1String(SERVICE_DESCRIPTION_KEY)); + if (service.description.isNull()) + bindValues.append(QLatin1String("")); + else + bindValues.append(service.description); + + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + +#ifdef QT_SFW_SERVICEDATABASE_GENERATE + statement = "INSERT INTO ServiceProperty(ServiceId,Key,Value) VALUES(?,?,?)"; + bindValues.clear(); + bindValues.append(serviceID); + bindValues.append(SERVICE_INITIALIZED_KEY); + bindValues.append(QString("NO")); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } +#endif + +#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + // Insert a security token for the particular service + statement = "INSERT INTO ServiceProperty(ServiceID,Key,Value) VALUES(?,?,?)"; + bindValues.clear(); + bindValues.append(serviceID); + bindValues.append(SECURITY_TOKEN_KEY); + bindValues.append(securityToken); + + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } +#endif // QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + + QList <QServiceInterfaceDescriptor> interfaces = service.interfaces; + QString interfaceID;; + foreach (const QServiceInterfaceDescriptor &interface, interfaces) { + interfaceID = getInterfaceID(&query, interface); + if (m_lastError.code() == DBError::NoError) { + QString errorText; + errorText = QLatin1String("Cannot register service \"%1\". \"%1\" is already registered " + "and implements interface \"%2\", Version \"%3.%4.\" \"%1\" must " + "first be deregistered for new registration to take place."); + m_lastError.setError(DBError::IfaceImplAlreadyRegistered, + errorText.arg(interface.serviceName()) + .arg(interface.interfaceName()) + .arg(interface.majorVersion()) + .arg(interface.minorVersion())); + + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return false; + } else if (m_lastError.code() == DBError::NotFound){ + //No interface implementation already exists for the service + //so add it + if (!insertInterfaceData(&query, interface, serviceID)) { + rollbackTransaction(&query); + return false; + } else { + continue; + } + } else { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << "Unable to confirm if implementation version" + << (QString::number(interface.majorVersion()) + "." + + QString::number(interface.minorVersion())).toAscii() + << "for interface" << interface.interfaceName() + << "is already registered for service " + << interface.serviceName() + << qPrintable(QString("\n") + m_lastError.text()); +#endif + return false; + } + } + + interfaces = service.latestInterfaces; + QServiceInterfaceDescriptor defaultInterface; + foreach (const QServiceInterfaceDescriptor &interface, interfaces) { + defaultInterface = interfaceDefault(interface.interfaceName(), NULL, true); + if (m_lastError.code() == DBError::NoError + || m_lastError.code() == DBError::ExternalIfaceIDFound) { + continue; //default already exists so don't do anything + } else if (m_lastError.code() == DBError::NotFound) { + //default does not already exist so create one + interfaceID = getInterfaceID(&query, interface); + if (m_lastError.code() != DBError::NoError) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << "Unable to retrieve interfaceID for " + "interface" << interface.interfaceName() + << qPrintable(QString("\n") + m_lastError.text()); +#endif + return false; + } + + statement = QLatin1String("INSERT INTO Defaults(InterfaceName, InterfaceID) VALUES(?,?)"); + bindValues.clear(); + bindValues.append(interface.interfaceName()); + bindValues.append(interfaceID); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } else { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::registerService()" + << "Problem: Unable to confirm if interface" + << interface.interfaceName() + << "already has a default implementation"; +#endif + return false; + } + } + + if (!commitTransaction(&query)) { + rollbackTransaction(&query); + return false; + } + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Obtains an interface ID corresponding to a given interface \a descriptor + + May set the following error codes: + DBError::NoError + DBError::NotFound + DBError::SqlError + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection +*/ +QString ServiceDatabase::getInterfaceID(const QServiceInterfaceDescriptor &descriptor) { + QString interfaceID; + if (!checkConnection()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::getInterfaceID():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return interfaceID; + } + + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + return getInterfaceID(&query, descriptor); +} + +/* + This function should only ever be called on a user scope database. + It returns a list of Interface Name and Interface ID pairs, where + the Interface ID refers to an external interface implementation + in the system scope database. + + May set the last error to: + DBError::NoError + DBError::SqlError + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection + + Aside: There is only one query which implicitly gets + wrapped in it's own transaction. +*/ +QList<QPair<QString,QString> > ServiceDatabase::externalDefaultsInfo() +{ + QList<QPair<QString,QString> > ret; + if (!checkConnection()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::externalDefaultsInfo():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return ret; + } + + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + //Prepare search query, bind criteria values and execute search + QString selectComponent = QLatin1String("SELECT InterfaceName, InterfaceID "); + QString fromComponent = QLatin1String("FROM Defaults "); + QString whereComponent = QLatin1String("WHERE InterfaceID NOT IN (SELECT Interface.ID FROM Interface) "); + + //Aside: this individual query is implicitly wrapped in a transaction + if (!executeQuery(&query, selectComponent + fromComponent + whereComponent)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::externalDefaultsInfo():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return ret; + } + + while (query.next()) { + ret.append(qMakePair(query.value(EBindIndex).toString(), + query.value(EBindIndex1).toString())); + } + + m_lastError.setError(DBError::NoError); + return ret; +} + +/* + Helper function that obtains an interfaceID for a given \a descriptor. + + May set last error to one of the following error codes: + DBError::NoError + DBError::NotFound + DBError::SqlError + + Aside: This function may be safely called standalone or within an explicit + transaction. If called standalone, it's single query is implicitly + wrapped in it's own transaction. +*/ +QString ServiceDatabase::getInterfaceID(QSqlQuery *query, const QServiceInterfaceDescriptor &interface) +{ + QString statement = QLatin1String("SELECT Interface.ID " + "FROM Interface, Service " + "WHERE Service.ID = Interface.ServiceID " + "AND Service.Name = ? COLLATE NOCASE " + "AND Interface.Name = ? COLLATE NOCASE " + "AND Interface.VerMaj = ? AND Interface.VerMin = ?"); + QList<QVariant> bindValues; + bindValues.append(interface.serviceName()); + bindValues.append(interface.interfaceName()); + bindValues.append(interface.majorVersion()); + bindValues.append(interface.minorVersion()); + + if (!executeQuery(query, statement, bindValues)) { + return QString(); + } + + if (!query->next()) { + QString errorText(QLatin1String("No Interface Descriptor found with " + "Service name: %1 " + "Interface name: %2 " + "Version: %3.%4")); + m_lastError.setError(DBError::NotFound, errorText.arg(interface.serviceName()) + .arg(interface.interfaceName()) + .arg(interface.majorVersion()) + .arg(interface.minorVersion())); + return QString(); + } + + m_lastError.setError(DBError::NoError); + return query->value(EBindIndex).toString(); +} + +/* + Helper functions that saves \a interface related data in the Interface table + The \a interface data is recorded as belonging to the service assocciated + with \a serviceID. + + May set the last error to one of the following error codes: + DBError::NoError + DBError::SqlError + + Aside: It is already assumed that a write transaction has been started by the + time this function is called; and this function will not rollback/commit + the transaction. +*/ +bool ServiceDatabase::insertInterfaceData(QSqlQuery *query,const QServiceInterfaceDescriptor &interface, const QString &serviceID) +{ + QString statement = QLatin1String("INSERT INTO Interface(ID, ServiceID,Name,VerMaj, VerMin) " + "VALUES(?,?,?,?,?)"); + QString interfaceID = QUuid::createUuid(); + + QList<QVariant> bindValues; + bindValues.append(interfaceID); + bindValues.append(serviceID); + bindValues.append(interface.interfaceName()); + bindValues.append(interface.majorVersion()); + bindValues.append(interface.minorVersion()); + + if (!executeQuery(query, statement, bindValues)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::insertInterfaceData():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + statement = QLatin1String("INSERT INTO InterfaceProperty(InterfaceID, Key, Value) VALUES(?,?,?)"); + QHash<QServiceInterfaceDescriptor::Attribute, QVariant>::const_iterator iter = interface.d->attributes.constBegin(); + bool isValidInterfaceProperty; + QString capabilities; + QString interfaceDescription; + while (iter != interface.d->attributes.constEnd()) { + isValidInterfaceProperty = true; + + bindValues.clear(); + bindValues.append(interfaceID); + switch (iter.key()) { + case (QServiceInterfaceDescriptor::Capabilities): + bindValues.append(QLatin1String(INTERFACE_CAPABILITY_KEY)); + capabilities = interface.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList().join(QLatin1String(",")); + if (capabilities.isNull()) + capabilities = QLatin1String(""); + bindValues.append(capabilities); + break; + case(QServiceInterfaceDescriptor::Location): + isValidInterfaceProperty = false; + break; + case(QServiceInterfaceDescriptor::ServiceDescription): + isValidInterfaceProperty = false; + break; + case(QServiceInterfaceDescriptor::InterfaceDescription): + bindValues.append(QLatin1String(INTERFACE_DESCRIPTION_KEY)); + interfaceDescription = interface.attribute(QServiceInterfaceDescriptor::InterfaceDescription).toString(); + if (interfaceDescription.isNull()) + interfaceDescription = QLatin1String(""); + bindValues.append(interfaceDescription); + break; + default: + isValidInterfaceProperty = false; + break; + } + + if (isValidInterfaceProperty) { + if (!executeQuery(query, statement, bindValues)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::insertInterfaceData():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } + ++iter; + } + + //add custom attributes + QHash<QString, QString>::const_iterator customIter = interface.d->customAttributes.constBegin(); + while (customIter!=interface.d->customAttributes.constEnd()) { + bindValues.clear(); + bindValues.append(interfaceID); + //to avoid key clashes use separate c_ namespace ->is this sufficient? + bindValues.append(QVariant("c_"+customIter.key())); + bindValues.append(customIter.value()); + if (!executeQuery(query, statement, bindValues)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::insertInterfaceData(customProps):-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + ++customIter; + } + m_lastError.setError(DBError::NoError); + + return true; +} + +/* + Helper function that executes the sql query specified in \a statement. + It is assumed that the \a statement uses positional placeholders and + corresponding parameters are placed in the list of \a bindValues. + + Aside: This function may be safely called standalone or within an explicit + transaction. If called standalone, it's single query is implicitly + wrapped in it's own transaction. + + May set the last error to one of the following error codes: + DBError::NoError + DBError::SqlError + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +bool ServiceDatabase::executeQuery(QSqlQuery *query, const QString &statement, const QList<QVariant> &bindValues) +{ + Q_ASSERT(query != NULL); + + bool success = false; + enum {Prepare =0 , Execute=1}; + for (int stage=Prepare; stage <= Execute; ++stage) { + if ( stage == Prepare) + success = query->prepare(statement); + else // stage == Execute + success = query->exec(); + + if (!success) { + QString errorText; + errorText = QLatin1String("Problem: Could not %1 statement: %2" + "Reason: %3" + "Parameters: %4\n"); + QString parameters; + if (bindValues.count() > 0) { + for(int i = 0; i < bindValues.count(); ++i) { + parameters.append(QLatin1String("\n\t[") + QString::number(i) + "]: " + bindValues.at(i).toString()); + } + } else { + parameters = QLatin1String("None"); + } + + DBError::ErrorCode errorType; + int result = query->lastError().number(); + if (result == 26 || result == 11) {//SQLILTE_NOTADB || SQLITE_CORRUPT + qWarning() << "Service Framework:- Database file is corrupt or invalid:" << databasePath(); + errorType = DBError::InvalidDatabaseFile; + } + else if ( result == 8) //SQLITE_READONLY + errorType = DBError::NoWritePermissions; + else + errorType = DBError::SqlError; + + m_lastError.setError(errorType, + errorText + .arg(stage == Prepare ?QLatin1String("prepare"):QLatin1String("execute")) + .arg(statement) + .arg(query->lastError().text()) + .arg(parameters)); + + query->finish(); + query->clear(); + return false; + } + + if (stage == Prepare) { + foreach (const QVariant &bindValue, bindValues) + query->addBindValue(bindValue); + } + } + + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Obtains a list of QServiceInterfaceDescriptors that match the constraints supplied + by \a filter. + + May set last error to one of the following error codes: + DBError::NoError + DBError::SqlError + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +QList<QServiceInterfaceDescriptor> ServiceDatabase::getInterfaces(const QServiceFilter &filter) +{ + QList<QServiceInterfaceDescriptor> interfaces; + if (!checkConnection()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::getInterfaces():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return interfaces; + } + + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + //multiple read queries are performed so wrap them + //in a read only transaction + if (!beginTransaction(&query, Read)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::getInterfaces():-" + << "Unable to begin transaction. " + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return interfaces; + } + + //Prepare search query, bind criteria values + QString selectComponent = QLatin1String("SELECT Interface.Name, " + "Service.Name, Interface.VerMaj, " + "Interface.VerMin, " + "Service.Location, " + "Service.ID, " + "Interface.ID "); + QString fromComponent = QLatin1String("FROM Interface, Service "); + QString whereComponent = QLatin1String("WHERE Service.ID = Interface.ServiceID "); + QList<QVariant> bindValues; + + if (filter.serviceName().isEmpty() && filter.interfaceName().isEmpty()) { + //do nothing, (don't add any extra constraints to the query + } else { + + if (!filter.serviceName().isEmpty()) { + whereComponent.append(QLatin1String("AND Service.Name = ?")).append(QLatin1String(" COLLATE NOCASE ")); + bindValues.append(filter.serviceName()); + } + if (!filter.interfaceName().isEmpty()) { + whereComponent.append(QLatin1String("AND Interface.Name = ?")).append(QLatin1String(" COLLATE NOCASE ")); + bindValues.append(filter.interfaceName()); + if (filter.majorVersion() >=0 && filter.minorVersion() >=0) { + if (filter.versionMatchRule() == QServiceFilter::ExactVersionMatch) { + whereComponent.append(QLatin1String("AND Interface.VerMaj = ?")).append(QLatin1String(" AND Interface.VerMin = ? ")); + bindValues.append(QString::number(filter.majorVersion())); + bindValues.append(QString::number(filter.minorVersion())); + } + else if (filter.versionMatchRule() == QServiceFilter::MinimumVersionMatch) { + whereComponent.append(QLatin1String("AND ((Interface.VerMaj > ?")) + .append(QLatin1String(") OR Interface.VerMaj = ?")).append(QLatin1String(" AND Interface.VerMin >= ?")).append(QLatin1String(") ")); + bindValues.append(QString::number(filter.majorVersion())); + bindValues.append(QString::number(filter.majorVersion())); + bindValues.append(QString::number(filter.minorVersion())); + } + } + } + } + + if (!executeQuery(&query, selectComponent + fromComponent + whereComponent, bindValues)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::getInterfaces():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + rollbackTransaction(&query); + return interfaces; + } + + QServiceInterfaceDescriptor interface; + interface.d = new QServiceInterfaceDescriptorPrivate; + QStringList capabilities; + QString serviceID; + QString interfaceID; + const QSet<QString> filterCaps = filter.capabilities().toSet(); + QSet<QString> difference; + + while (query.next()){ + difference.clear(); + interface.d->customAttributes.clear(); + interface.d->attributes.clear(); + interface.d->interfaceName = query.value(EBindIndex).toString(); + interface.d->serviceName = query.value(EBindIndex1).toString(); + interface.d->major = query.value(EBindIndex2).toInt(); + interface.d->minor = query.value(EBindIndex3).toInt(); + + QString location = query.value(EBindIndex4).toString(); + if (location.startsWith(QLatin1String(SERVICE_IPC_PREFIX))) { + interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::InterProcess; + interface.d->attributes[QServiceInterfaceDescriptor::Location] + = location.remove(0,QString(QLatin1String(SERVICE_IPC_PREFIX)).size()); + } else { + interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::Plugin; + interface.d->attributes[QServiceInterfaceDescriptor::Location] = location; + } + + serviceID = query.value(EBindIndex5).toString(); + if (!populateServiceProperties(&interface, serviceID)) { + //populateServiceProperties should already give a warning message + //and set the last error + interfaces.clear(); + rollbackTransaction(&query); + return interfaces; + } + + interfaceID = query.value(EBindIndex6).toString(); + if (!populateInterfaceProperties(&interface, interfaceID)) { + //populateInterfaceProperties should already give a warning message + //and set the last error + interfaces.clear(); + rollbackTransaction(&query); + return interfaces; + } + + const QSet<QString> ifaceCaps = interface.d->attributes.value(QServiceInterfaceDescriptor::Capabilities).toStringList().toSet(); + difference = ((filter.capabilityMatchRule() == QServiceFilter::MatchMinimum) ? (filterCaps-ifaceCaps) : (ifaceCaps-filterCaps)); + if (!difference.isEmpty()) + continue; + + //only return those interfaces that comply with set custom filters + if (filter.customAttributes().size() > 0) { + QSet<QString> keyDiff = filter.customAttributes().toSet(); + keyDiff.subtract(interface.d->customAttributes.uniqueKeys().toSet()); + if (keyDiff.isEmpty()) { //target descriptor has same custom keys as filter + bool isMatch = true; + const QStringList keys = filter.customAttributes(); + for (int i = 0; i<keys.count(); i++) { + if (interface.d->customAttributes.value(keys[i]) != + filter.customAttribute(keys[i])) { + isMatch = false; + break; + } + } + if (isMatch) + interfaces.append(interface); + } + } else { //no custom keys -> SQL statement ensures proper selection already + interfaces.append(interface); + } + } + + rollbackTransaction(&query);//read-only operation so just rollback + m_lastError.setError(DBError::NoError); + return interfaces; +} + +/* + Obtains a QServiceInterfaceDescriptor that + corresponds to a given \a interfaceID + + May set last error to one of the following error codes: + DBError::NoError + DBError::NotFound + DBError::SqlError + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +QServiceInterfaceDescriptor ServiceDatabase::getInterface(const QString &interfaceID) +{ + QServiceInterfaceDescriptor interface; + if (!checkConnection()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::getInterface():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return interface; + } + + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + if (!beginTransaction(&query, Read)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::getInterface():-" + << "Unable to begin transaction. " + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return interface; + } + + QString selectComponent = QLatin1String("SELECT Interface.Name, " + "Service.Name, Interface.VerMaj, " + "Interface.VerMin, " + "Service.Location, " + "Service.ID "); + QString fromComponent = QLatin1String("FROM Interface, Service "); + QString whereComponent = QLatin1String("WHERE Service.ID = Interface.ServiceID " + "AND Interface.ID = ? "); + QList<QVariant> bindValues; + bindValues.append(interfaceID); + + if (!executeQuery(&query, selectComponent + fromComponent + whereComponent, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::getInterfaces():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return interface; + } + + if (!query.next()) { + rollbackTransaction(&query); + QString errorText(QLatin1String("Interface implementation not found for Interface ID: %1")); + m_lastError.setError(DBError::NotFound, errorText.arg(interfaceID)); + return interface; + } + + interface.d = new QServiceInterfaceDescriptorPrivate; + interface.d->interfaceName =query.value(EBindIndex).toString(); + interface.d->serviceName = query.value(EBindIndex1).toString(); + interface.d->major = query.value(EBindIndex2).toInt(); + interface.d->minor = query.value(EBindIndex3).toInt(); + + QString location = query.value(EBindIndex4).toString(); + if (location.startsWith(QLatin1String(SERVICE_IPC_PREFIX))) { + interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::InterProcess; + interface.d->attributes[QServiceInterfaceDescriptor::Location] + = location.remove(0,QString(QLatin1String(SERVICE_IPC_PREFIX)).size()); + } else { + interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::Plugin; + interface.d->attributes[QServiceInterfaceDescriptor::Location] = location; + } + + QString serviceID = query.value(EBindIndex5).toString(); + if (!populateServiceProperties(&interface, serviceID)) { + //populateServiceProperties should already give a warning message + //and set the last error + rollbackTransaction(&query); + return QServiceInterfaceDescriptor(); + } + + if (!populateInterfaceProperties(&interface, interfaceID)) { + //populateInterfaceProperties should already give a warning message + //and set the last error + rollbackTransaction(&query); + return QServiceInterfaceDescriptor(); + } + + rollbackTransaction(&query);//read only operation so just rollback + m_lastError.setError(DBError::NoError); + return interface; +} + +/* + Obtains a list of services names. If \a interfaceName is empty, + then all service names are returned. If \a interfaceName specifies + an interface then the names of all services implementing that interface + are returned + + May set last error to one of the following error codes: + DBError::NoError + DBError::SqlError + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection + DBError::NoWritePermissions + DBError::InvalidDatabaseFile + + Aside: There is only one query which implicitly gets + wrapped in it's own transaction. +*/ +QStringList ServiceDatabase::getServiceNames(const QString &interfaceName) +{ + QStringList services; + if (!checkConnection()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::getServiceNames():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return services; + } + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + QString selectComponent(QLatin1String("SELECT DISTINCT Service.Name COLLATE NOCASE ")); + QString fromComponent; + QString whereComponent; + QList<QVariant> bindValues; + if (interfaceName.isEmpty()) { + fromComponent = QLatin1String("FROM Service "); + } else { + fromComponent = QLatin1String("FROM Interface,Service "); + whereComponent = QLatin1String("WHERE Service.ID = Interface.ServiceID AND Interface.Name = ? COLLATE NOCASE "); + bindValues.append(interfaceName); + } + + if (!executeQuery(&query, selectComponent + fromComponent + whereComponent, bindValues)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::getServiceNames():-" + << qPrintable(m_lastError.text()); +#endif + return services; + } + + while ( query.next()) { + services.append(query.value(EBindIndex).toString()); + } + query.finish(); + query.clear(); + m_lastError.setError(DBError::NoError); + return services; +} + +/* + Returns a descriptor for the default interface implementation of + \a interfaceName. + + For user scope databases only, \a defaultInterfaceID is set if the default + in the user scope database refers to a interface implementation in the + system scope database. In this case the descriptor will be invalid and + the \a defaultInterfaceID must be used to query the system scope database, + The last error set to DBError::ExternalIfaceIDFound + + If this function is called within a transaction, \a inTransaction + must be set to true. If \a inTransaction is false, this fuction + will begin and end its own transaction. + + The last error may be set to one of the following error codes: + DBError::NoError + DBError::ExternalIfaceIDFound + DBError::SqlError + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +QServiceInterfaceDescriptor ServiceDatabase::interfaceDefault(const QString &interfaceName, QString *defaultInterfaceID, + bool inTransaction) +{ + QServiceInterfaceDescriptor interface; + if (!checkConnection()) + { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::interfaceDefault():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return interface; + } + + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + if (!inTransaction && !beginTransaction(&query, Read)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::interfaceDefault(QString, QString):-" + << "Unable to begin transaction. " + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return interface; + } + + QString statement(QLatin1String("SELECT InterfaceID FROM Defaults WHERE InterfaceName = ? COLLATE NOCASE")); + QList<QVariant> bindValues; + bindValues.append(interfaceName); + if (!executeQuery(&query, statement, bindValues)) { + if (!inTransaction) + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::interfaceDefault():-" + << qPrintable(m_lastError.text()); +#endif + return interface; + } + + QString interfaceID; + if (!query.next()) + { + if (!inTransaction) + rollbackTransaction(&query); + QString errorText(QLatin1String("No default service found for interface: \"%1\"")); + m_lastError.setError(DBError::NotFound, errorText.arg(interfaceName)); + return interface; + } + else + interfaceID = query.value(EBindIndex).toString(); + Q_ASSERT(!interfaceID.isEmpty()); + + statement = QLatin1String("SELECT Interface.Name, " + "Service.Name, Interface.VerMaj, " + "Interface.VerMin, " + "Service.Location, " + "Service.ID " + "FROM Service, Interface " + "WHERE Service.ID = Interface.ServiceID AND Interface.ID = ?"); + bindValues.clear(); + bindValues.append(interfaceID); + if (!executeQuery(&query, statement, bindValues)) + { + if (!inTransaction) + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::interfaceDefault():-" + << qPrintable(m_lastError.text()); +#endif + return interface; + } + + if (!query.next()) { + if (!inTransaction) + rollbackTransaction(&query); + if (defaultInterfaceID != NULL ) + *defaultInterfaceID = interfaceID; + m_lastError.setError(DBError::ExternalIfaceIDFound); + return interface; + } + + interface.d = new QServiceInterfaceDescriptorPrivate; + interface.d->interfaceName =query.value(EBindIndex).toString(); + interface.d->serviceName = query.value(EBindIndex1).toString(); + interface.d->major = query.value(EBindIndex2).toInt(); + interface.d->minor = query.value(EBindIndex3).toInt(); + + QString location = query.value(EBindIndex4).toString(); + if (location.startsWith(QLatin1String(SERVICE_IPC_PREFIX))) { + interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::InterProcess; + interface.d->attributes[QServiceInterfaceDescriptor::Location] + = location.remove(0,QString(QLatin1String(SERVICE_IPC_PREFIX)).size()); + } else { + interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::Plugin; + interface.d->attributes[QServiceInterfaceDescriptor::Location] = location; + } + + QString serviceID = query.value(EBindIndex5).toString(); + if (!populateServiceProperties(&interface, serviceID)) { + //populateServiceProperties should already give a warning + //and set the last error + if (!inTransaction) + rollbackTransaction(&query); + return QServiceInterfaceDescriptor(); + } + + if (!populateInterfaceProperties(&interface, interfaceID)) { + //populateInterfaceProperties should already give a warning + //and set the last error + if (!inTransaction) + rollbackTransaction(&query); + return QServiceInterfaceDescriptor(); + } + + if (!inTransaction) + rollbackTransaction(&query); //Read only operation so just rollback + m_lastError.setError(DBError::NoError); + return interface; +} + +/* + Sets a particular service's \a interface implementation as a the default + implementation to look up when using the interface's name in + interfaceDefault(). + + For a user scope database an \a externalInterfaceID can be provided + so that the Defaults table will contain a "link" to an interface + implmentation provided in the system scope database. + + May set the last error to one of the following error codes: + DBError::NoError + DBerror::NotFound + DBError::SqlError + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +bool ServiceDatabase::setInterfaceDefault(const QServiceInterfaceDescriptor &interface, const QString &externalInterfaceID) +{ + if (!checkConnection()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + //Begin Transaction + if (!beginTransaction(&query, Write)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-" + << "Problem: Unable to begin transaction." + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + QString statement; + QList<QVariant> bindValues; + QString interfaceID = externalInterfaceID; + if (interfaceID.isEmpty()) { + statement = QLatin1String("SELECT Interface.ID from Interface, Service " + "WHERE Service.ID = Interface.ServiceID " + "AND Service.Name = ? COLLATE NOCASE " + "AND Interface.Name = ? COLLATE NOCASE " + "AND Interface.VerMaj = ? " + "AND Interface.VerMin = ? "); + bindValues.append(interface.serviceName()); + bindValues.append(interface.interfaceName()); + bindValues.append(interface.majorVersion()); + bindValues.append(interface.minorVersion()); + + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + if (!query.next()) { + QString errorText; + errorText = QLatin1String("No implementation for interface: %1, Version: %2.%3 found " + "for service: %4"); + m_lastError.setNotFoundError(errorText.arg(interface.interfaceName()) + .arg(interface.majorVersion()) + .arg(interface.minorVersion()) + .arg(interface.serviceName())); + + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatbase::setInterfaceDefault(QServiceInterfaceDescriptor):-" + << "Problem: Unable to set default service. " + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + interfaceID = query.value(EBindIndex).toString(); + Q_ASSERT(!interfaceID.isEmpty()); + } + + statement = QLatin1String("SELECT InterfaceName FROM Defaults WHERE InterfaceName = ? COLLATE NOCASE"); + bindValues.clear(); + bindValues.append(interface.interfaceName()); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + if (query.next()) { + statement = QLatin1String("UPDATE Defaults " + "SET InterfaceID = ? " + "WHERE InterfaceName = ? COLLATE NOCASE"); + bindValues.clear(); + bindValues.append(interfaceID); + bindValues.append(interface.interfaceName()); + + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } else { + statement = QLatin1String("INSERT INTO Defaults(InterfaceName,InterfaceID) VALUES(?,?)"); + bindValues.clear(); + bindValues.append(interface.interfaceName()); + bindValues.append(interfaceID); + + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } + + //End Transaction + if (!commitTransaction(&query)) { + rollbackTransaction(&query); + return false; + } + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Removes the service with name \a serviceName. + If the service provides a default interface implementation, then + another service implementing the highest interface implementation + version becomes the new default(if any). If more than one service + provides same the highest version number, an arbitrary choice is made + between them. + + May set the last error to the folowing error codes: + DBError::NoError + DBError::NotFound + DBError::SqlError + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +bool ServiceDatabase::unregisterService(const QString &serviceName, const QString &securityToken) +{ +#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + Q_UNUSED(securityToken); +#else + if (securityToken.isEmpty()) { + QString errorText(QLatin1String("Access denied, no security token provided (for unregistering service: \"%1\")")); + m_lastError.setError(DBError::NoWritePermissions, errorText.arg(serviceName)); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << "Problem: Unable to unregister service. " + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } +#endif + + + if (!checkConnection()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + if (!beginTransaction(&query, Write)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << "Problem: Unable to begin transaction. " + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + QString statement(QLatin1String("SELECT Service.ID from Service WHERE Service.Name = ? COLLATE NOCASE")); + QList<QVariant> bindValues; + bindValues.append(serviceName); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + QStringList serviceIDs; + while (query.next()) { + serviceIDs << query.value(EBindIndex).toString(); + } + +#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + // Only the application that registered the service is allowed to unregister that + // service. Fetch a security ID of a service (with given name) and verify that it matches + // with current apps security id. Only one application is allowed to register services with + // same name, hence a distinct (just any of the) security token will do because they are identical. + if (!serviceIDs.isEmpty()) { + statement = QLatin1String("SELECT DISTINCT Value FROM ServiceProperty WHERE ServiceID = ? AND Key = ?"); + bindValues.clear(); + bindValues.append(serviceIDs.first()); + bindValues.append(SECURITY_TOKEN_KEY); + + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + QString existingSecurityToken; + if (query.next()) { + existingSecurityToken = query.value(EBindIndex).toString(); + } + if (existingSecurityToken != securityToken) { + QString errorText(QLatin1String("Access denied: \"%1\"")); + m_lastError.setError(DBError::NoWritePermissions, errorText.arg(serviceName)); + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << "Problem: Unable to unregister service" + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + } +#endif // QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + + statement = QLatin1String("SELECT Interface.ID from Interface, Service " + "WHERE Interface.ServiceID = Service.ID " + "AND Service.Name =? COLLATE NOCASE"); + bindValues.clear(); + bindValues.append(serviceName); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + QStringList interfaceIDs; + while (query.next()) { + interfaceIDs << query.value(EBindIndex).toString(); + } + + if (serviceIDs.count() == 0) { + QString errorText(QLatin1String("Service not found: \"%1\"")); + m_lastError.setError(DBError::NotFound, errorText.arg(serviceName)); + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << "Problem: Unable to unregister service" + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + statement = QLatin1String("SELECT Defaults.InterfaceName " + "FROM Defaults, Interface, Service " + "WHERE Defaults.InterfaceID = Interface.ID " + "AND Interface.ServiceID = Service.ID " + "AND Service.Name = ? COLLATE NOCASE"); + bindValues.clear(); + bindValues.append(serviceName); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase:unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + QStringList serviceDefaultInterfaces; + while (query.next()) { + serviceDefaultInterfaces << query.value(EBindIndex).toString(); + } + + + statement = QLatin1String("DELETE FROM Service WHERE Service.Name = ? COLLATE NOCASE"); + bindValues.clear(); + bindValues.append(serviceName); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + statement = QLatin1String("DELETE FROM Interface WHERE Interface.ServiceID = ?"); + foreach (const QString &serviceID, serviceIDs) { + bindValues.clear(); + bindValues.append(serviceID); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } + + statement = QLatin1String("DELETE FROM ServiceProperty WHERE ServiceID = ?"); + + foreach (const QString &serviceID, serviceIDs) { + bindValues.clear(); + bindValues.append(serviceID); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } + + statement = QLatin1String("DELETE FROM InterfaceProperty WHERE InterfaceID = ?"); + foreach (const QString &interfaceID, interfaceIDs) { + bindValues.clear(); + bindValues.append(interfaceID); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } + + foreach (const QString &interfaceName, serviceDefaultInterfaces) { + statement = QLatin1String("SELECT ID FROM Interface WHERE Interface.Name = ? COLLATE NOCASE " + "ORDER BY Interface.VerMaj DESC, Interface.VerMin DESC"); + bindValues.clear(); + bindValues.append(interfaceName); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + if (query.next()) { + QString newDefaultID = query.value(EBindIndex).toString(); + statement = QLatin1String("UPDATE Defaults SET InterfaceID = ? WHERE InterfaceName = ? COLLATE NOCASE "); + bindValues.clear(); + bindValues.append(newDefaultID); + bindValues.append(interfaceName); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } else { + statement = QLatin1String("DELETE FROM Defaults WHERE InterfaceName = ? COLLATE NOCASE "); + bindValues.clear(); + bindValues.append(interfaceName); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } + } + + //databaseCommit + if (!commitTransaction(&query)) { + rollbackTransaction(&query); + return false; + } + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Registers the service initialization into the database. +*/ +bool ServiceDatabase::serviceInitialized(const QString &serviceName, const QString &securityToken) +{ +#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + Q_UNUSED(securityToken); +#endif + + if (!checkConnection()) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + if (!beginTransaction(&query, Write)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::serviceInitialized():-" + << "Problem: Unable to begin transaction" + << "\nReason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + QString statement(QLatin1String("SELECT Service.ID from Service WHERE Service.Name = ? COLLATE NOCASE")); + QList<QVariant> bindValues; + bindValues.append(serviceName); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::serviceInitialized():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + QStringList serviceIDs; + while(query.next()) { + serviceIDs << query.value(EBindIndex).toString(); + } + + +#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN + statement = QLatin1String("SELECT Value FROM ServiceProperty WHERE ServiceID = ? AND Key = ?"); + bindValues.clear(); + bindValues.append(serviceName); + bindValues.append(SECURITY_TOKEN_KEY); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::unregisterService():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + QStringList securityTokens; + while (query.next()) { + securityTokens << query.value(EBindIndex).toString(); + } + + if (!securityTokens.isEmpty() && (securityTokens.first() != securityToken)) { + QString errorText(QLatin1String("Access denied: \"%1\"")); + m_lastError.setError(DBError::NoWritePermissions, errorText.arg(serviceName)); + rollbackTransaction(&query); + #ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::serviceInitialized():-" + << "Problem: Unable to update service initialization" + << "\nReason:" << qPrintable(m_lastError.text()); + #endif + } +#endif + + statement = QLatin1String("DELETE FROM ServiceProperty WHERE ServiceID = ? AND Key = ?"); + foreach (const QString &serviceID, serviceIDs) { + bindValues.clear(); + bindValues.append(serviceID); + bindValues.append(QLatin1String(SERVICE_INITIALIZED_KEY)); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::serviceInitialized():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } + + //databaseCommit + if (!commitTransaction(&query)) { + rollbackTransaction(&query); + return false; + } + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Closes the database + + May set the following error codes: + DBError::NoError + DBError::InvalidDatabaseConnection +*/ +bool ServiceDatabase::close() +{ + if (m_isDatabaseOpen) { + QSqlDatabase database = QSqlDatabase::database(m_connectionName, false); + if (database.isValid()) { + if (database.isOpen()) { + database.close(); + m_isDatabaseOpen = false; + return true; + } + } else { + m_lastError.setError(DBError::InvalidDatabaseConnection); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::close():-" + << "Problem: " << qPrintable(m_lastError.text()); +#endif + return false; + } + } + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Sets the path of the service database to \a databasePath +*/ +void ServiceDatabase::setDatabasePath(const QString &databasePath) +{ + m_databasePath = QDir::toNativeSeparators(databasePath); +} + +/* + Returns the path of the service database +*/ +QString ServiceDatabase::databasePath() const +{ + QString path; + if (m_databasePath.isEmpty()) { +#ifdef Q_OS_SYMBIAN + QString qtVersion(qVersion()); + qtVersion = qtVersion.left(qtVersion.size() -2); //strip off patch version + path = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/QtServiceFramework_" + + qtVersion + "_system" + QLatin1String(".db")); +#else + QSettings settings(QSettings::SystemScope, QLatin1String("Nokia"), QLatin1String("Services")); + path = settings.value(QLatin1String("ServicesDB/Path")).toString(); + if (path.isEmpty()) { + path = QDir::currentPath(); + if (path.lastIndexOf(QLatin1String(RESOLVERDATABASE_PATH_SEPARATOR)) != path.length() -1) { + path.append(QLatin1String(RESOLVERDATABASE_PATH_SEPARATOR)); + } + path.append(QLatin1String(RESOLVERDATABASE)); + } + path = QDir::toNativeSeparators(path); +#endif + } else { + path = m_databasePath; + } + + return path; +} + +/* + Helper method that creates the database tables: Service, Interface, + Defaults, ServiceProperty and InterfaceProperty + + May set the last error to one of the following error codes: + DBError::NoError + DBError::SqlError + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +bool ServiceDatabase::createTables() +{ + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + //Begin Transaction + if (!beginTransaction(&query, Write)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::createTables():-" + << "Unable to begin transaction. " + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + QString statement(QLatin1String("CREATE TABLE Service(" + "ID TEXT NOT NULL PRIMARY KEY UNIQUE," + "Name TEXT NOT NULL, " + "Location TEXT NOT NULL)")); + if (!executeQuery(&query, statement)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::createTables():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + statement = QLatin1String("CREATE TABLE Interface(" + "ID TEXT NOT NULL PRIMARY KEY UNIQUE," + "ServiceID TEXT NOT NULL, " + "Name TEXT NOT NULL, " + "VerMaj INTEGER NOT NULL, " + "VerMin INTEGER NOT NULL)"); + if (!executeQuery(&query, statement)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::createTables():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + statement = QLatin1String("CREATE TABLE Defaults(" + "InterfaceName TEXT PRIMARY KEY UNIQUE NOT NULL," + "InterfaceID TEXT NOT NULL)"); + if (!executeQuery(&query, statement)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::createTables():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + statement = QLatin1String("CREATE TABLE ServiceProperty(" + "ServiceID TEXT NOT NULL," + "Key TEXT NOT NULL," + "Value TEXT NOT NULL)"); + if (!executeQuery(&query, statement)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::createTables():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + statement = QLatin1String("CREATE TABLE InterfaceProperty(" + "InterfaceID TEXT NOT NULL," + "Key TEXT NOT NULL," + "Value TEXT NOT NULL)"); + + if (!executeQuery(&query, statement)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::createTables():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + if (!commitTransaction(&query)) { + rollbackTransaction(&query); + return false; + } + m_lastError.setError(DBError::NoError); + return true; +} + +/*! + Helper method that checks if the all expected tables exist in the database + Returns true if they all exist and false if any of them don't +*/ +bool ServiceDatabase::checkTables() +{ + bool bTables(false); + QStringList tables = QSqlDatabase::database(m_connectionName).tables(); + if (tables.contains(QLatin1String(SERVICE_TABLE)) + && tables.contains(QLatin1String(INTERFACE_TABLE)) + && tables.contains(QLatin1String(DEFAULTS_TABLE)) + && tables.contains(QLatin1String(SERVICE_PROPERTY_TABLE)) + && tables.contains(QLatin1String(INTERFACE_PROPERTY_TABLE))){ + bTables = true; + } + return bTables; +} + +/* + This function should only ever be used on a user scope database + It removes an entry from the Defaults table where the default + refers to an interface implementation in the system scope database. + The particular default that is removed is specified by + \a interfaceID. + + May set the last error to one of the following error codes: + DBError::NoError + DBError::IfaceIDNotExternal + DBError::SqlError + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +bool ServiceDatabase::removeExternalDefaultServiceInterface(const QString &interfaceID) +{ + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + + //begin transaction + if (!beginTransaction(&query, Write)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::removeExternalDefaultServiceInterface():-" + << "Problem: Unable to begin transaction. " + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + + QString statement(QLatin1String("SELECT Name FROM Interface WHERE Interface.ID = ?")); + QList<QVariant> bindValues; + bindValues.append(interfaceID); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::removeDefaultServiceInterface():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + if (query.next()) { + QString interfaceName = query.value(EBindIndex).toString(); + QString errorText(QLatin1String("Local interface implementation exists for interface \"%1\" " + "with interfaceID: \"%2\"")); + m_lastError.setError(DBError::IfaceIDNotExternal, + errorText.arg(interfaceName).arg(interfaceID)); + rollbackTransaction(&query); + return false; + } + + statement = QLatin1String("DELETE FROM Defaults WHERE InterfaceID = ? COLLATE NOCASE"); + bindValues.clear(); + bindValues.append(interfaceID); + if (!executeQuery(&query, statement, bindValues)) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::removeDefaultServiceInterface():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + //end transaction + if (!commitTransaction(&query)){ + rollbackTransaction(&query); + return false; + } + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Removes all tables from the database + + In future this function may be deprecated or removed. + + May set the last error to one of the following error codes: + DBError::NoError + DBError::SqlError + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +bool ServiceDatabase::dropTables() +{ + //Execute transaction for deleting the database tables + QSqlDatabase database = QSqlDatabase::database(m_connectionName); + QSqlQuery query(database); + QStringList expectedTables; + expectedTables << QLatin1String(SERVICE_TABLE) + << QLatin1String(INTERFACE_TABLE) + << QLatin1String(DEFAULTS_TABLE) + << QLatin1String(SERVICE_PROPERTY_TABLE) + << QLatin1String(INTERFACE_PROPERTY_TABLE); + + if (database.tables().count() > 0) { + if (!beginTransaction(&query, Write)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::dropTables():-" + << "Unable to begin transaction. " + << "Reason:" << qPrintable(m_lastError.text()); +#endif + return false; + } + QStringList actualTables = database.tables(); + + foreach (const QString expectedTable, expectedTables) { + if ((actualTables.contains(expectedTable)) + && (!executeQuery(&query, QLatin1String("DROP TABLE ") + expectedTable))) { + rollbackTransaction(&query); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::dropTables():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + } + if (!commitTransaction(&query)) { + rollbackTransaction(&query); + return false; + } + } + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Checks if the database is open +*/ +bool ServiceDatabase::isOpen() const +{ + return m_isDatabaseOpen; +} + +/* + Checks the database connection. + + May set the last error to one of the following error codes: + DBError::DatabaseNotOpen + DBError::InvalidDatabaseConnection +*/ +bool ServiceDatabase::checkConnection() +{ + if (!m_isDatabaseOpen) + { + m_lastError.setError(DBError::DatabaseNotOpen); + return false; + } + + if (!QSqlDatabase::database(m_connectionName).isValid()) + { + m_lastError.setError(DBError::InvalidDatabaseConnection); + return false; + } + + return true; +} + +/* + Begins a transcaction based on the \a type which can be Read or Write. + + May set the last error to one of the following error codes: + DBError::NoError + DBError::SqlError + DBError::NoWritePermissions + DBError::InvalidDatabaseFile +*/ +bool ServiceDatabase::beginTransaction(QSqlQuery *query, TransactionType type) +{ + bool success; + if (type == Read) + success = query->exec(QLatin1String("BEGIN")); + else + success = query->exec(QLatin1String("BEGIN IMMEDIATE")); + + if (!success) { + int result = query->lastError().number(); + if (result == 26 || result == 11) {//SQLITE_NOTADB || SQLITE_CORRUPT + qWarning() << "Service Framework:- Database file is corrupt or invalid:" << databasePath(); + m_lastError.setError(DBError::InvalidDatabaseFile, query->lastError().text()); + } + else if (result == 8) { //SQLITE_READONLY + qWarning() << "Service Framework:- Insufficient permissions to write to database:" << databasePath(); + m_lastError.setError(DBError::NoWritePermissions, query->lastError().text()); + } + else + m_lastError.setError(DBError::SqlError, query->lastError().text()); + return false; + } + + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Commits a transaction + + May set the last error to one of the following error codes: + DBError::NoError + DBError::SqlError +*/ +bool ServiceDatabase::commitTransaction(QSqlQuery *query) +{ + Q_ASSERT(query != NULL); + query->finish(); + query->clear(); + if (!query->exec(QLatin1String("COMMIT"))) { + m_lastError.setError(DBError::SqlError, query->lastError().text()); + return false; + } + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Rolls back a transaction + + May set the last error to one of the following error codes: + DBError::NoError + DBError::SqlError +*/ +bool ServiceDatabase::rollbackTransaction(QSqlQuery *query) +{ + Q_ASSERT(query !=NULL); + query->finish(); + query->clear(); + + if (!query->exec(QLatin1String("ROLLBACK"))) { + m_lastError.setError(DBError::SqlError, query->lastError().text()); + return false; + } + return true; +} + +/* + Helper function that populates a service \a interface descriptor + with interface related attributes corresponding to the interface + represented by \a interfaceID + + It is already assumed that a transaction has been started by the time + this function is called. This function will not rollback/commit the + transaction. + + May set the last error to one of the following error codes: + DBError::NoError + DBError::SqlError +*/ +bool ServiceDatabase::populateInterfaceProperties(QServiceInterfaceDescriptor *interface, const QString &interfaceID) +{ + QSqlQuery query(QSqlDatabase::database(m_connectionName)); + QString statement(QLatin1String("SELECT Key, Value FROM InterfaceProperty WHERE InterfaceID = ?")); + QList<QVariant> bindValues; + bindValues.append(interfaceID); + if (!executeQuery(&query, statement, bindValues)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::populateInterfaceProperties():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + bool isFound = false; + QString attribute; + while (query.next()) { + isFound = true; + attribute = query.value(EBindIndex).toString(); + if (attribute == QLatin1String(INTERFACE_CAPABILITY_KEY)) { + const QStringList capabilities = query.value(EBindIndex1).toString().split(QLatin1String(",")); + if (capabilities.count() == 1 && capabilities[0].isEmpty()) { + interface->d->attributes[QServiceInterfaceDescriptor::Capabilities] + = QStringList(); + } else { + interface->d->attributes[QServiceInterfaceDescriptor::Capabilities] + = capabilities; + } + } else if (attribute == QLatin1String(INTERFACE_DESCRIPTION_KEY)) { + interface->d->attributes[QServiceInterfaceDescriptor::InterfaceDescription] + = query.value(EBindIndex1).toString(); + } else if (attribute.startsWith(QLatin1String("c_"))) { + interface->d->customAttributes[attribute.mid(2)] + = query.value(EBindIndex1).toString(); + } + } + + if (!isFound) { + QString errorText(QLatin1String("Database integrity corrupted, Properties for InterfaceID: %1 does not exist in the InterfaceProperty table for interface \"%2\"")); + m_lastError.setError(DBError::SqlError, errorText.arg(interfaceID).arg(interface->interfaceName())); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::populateInterfaceProperties():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return false; + } + m_lastError.setError(DBError::NoError); + return true; +} + +/* + Helper function that populates a service \a interface descriptor + with service related attributes corresponding to the service + represented by \a serviceID + + It is already assumed that a transaction has been started by the time + this function is called. This function will not rollback/commit the + transaction. + + May set the last error to one of the following error codes: + DBError::NoError + DBError::SqlError +*/ +bool ServiceDatabase::populateServiceProperties(QServiceInterfaceDescriptor *interface, const QString &serviceID) +{ + QSqlQuery query(QSqlDatabase::database(m_connectionName)); + QString statement(QLatin1String("SELECT Key, Value FROM ServiceProperty WHERE ServiceID = ?")); + QList<QVariant> bindValues; + bindValues.append(serviceID); + if (!executeQuery(&query, statement, bindValues)) { +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::populateServiceProperties():-" + << qPrintable(m_lastError.text()); +#endif + return false; + } + + bool isFound = false; + QString attribute; + while (query.next()) { + isFound = true; + attribute = query.value(EBindIndex).toString(); + if (attribute == QLatin1String(SERVICE_DESCRIPTION_KEY)) { + interface->d->attributes[QServiceInterfaceDescriptor::ServiceDescription] + = query.value(EBindIndex1).toString(); + } + // fetch initialized and put it as a custom attribute + if (attribute == QLatin1String(SERVICE_INITIALIZED_KEY)) { + interface->d->customAttributes[attribute] = query.value(EBindIndex1).toString(); + } + } + + if (!isFound) { + QString errorText(QLatin1String("Database integrity corrupted, Service Properties for ServiceID: \"%1\" does not exist in the ServiceProperty table for service \"%2\"")); + m_lastError.setError(DBError::SqlError, errorText.arg(serviceID).arg(interface->serviceName())); +#ifdef QT_SFW_SERVICEDATABASE_DEBUG + qWarning() << "ServiceDatabase::populateServiceProperties():-" + << "Problem:" << qPrintable(m_lastError.text()); +#endif + return false; + } + m_lastError.setError(DBError::NoError); + return true; +} + +#include "moc_servicedatabase_p.cpp" + +QTM_END_NAMESPACE diff --git a/src/serviceframework/servicedatabase_p.h b/src/serviceframework/servicedatabase_p.h new file mode 100644 index 00000000..ff8fac30 --- /dev/null +++ b/src/serviceframework/servicedatabase_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERVICEDATABASE_H_ +#define QSERVICEDATABASE_H_ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qserviceframeworkglobal.h" +#include <QtSql> +#include <QList> +#include "servicemetadata_p.h" +#include "qservicefilter.h" +#include "dberror_p.h" + + +QT_BEGIN_HEADER +QTM_BEGIN_NAMESPACE + +class QServiceInterfaceDescriptor; + +class Q_AUTOTEST_EXPORT ServiceDatabase : public QObject +{ + Q_OBJECT + + public: + ServiceDatabase(void); + + virtual ~ServiceDatabase(); + + bool open(); + bool close(); + + bool isOpen() const; + void setDatabasePath(const QString &databasePath); + QString databasePath() const; + + bool registerService(const ServiceMetaDataResults &service, const QString &securityToken = QString()); + bool unregisterService(const QString &serviceName, const QString &securityToken = QString()); + bool serviceInitialized(const QString &serviceName, const QString &securityToken = QString()); + + QList<QServiceInterfaceDescriptor> getInterfaces(const QServiceFilter &filter); + QServiceInterfaceDescriptor getInterface(const QString &interfaceID); + QString getInterfaceID(const QServiceInterfaceDescriptor &interface); + QStringList getServiceNames(const QString &interfaceName); + + QServiceInterfaceDescriptor interfaceDefault(const QString &interfaceName, + QString *interfaceID = 0, bool inTransaction = false); + bool setInterfaceDefault(const QServiceInterfaceDescriptor &interface, + const QString &externalInterfaceID = QString()); + QList<QPair<QString,QString> > externalDefaultsInfo(); + bool removeExternalDefaultServiceInterface(const QString &interfaceID); + + DBError lastError() const { return m_lastError; } + +Q_SIGNALS: + void serviceAdded(const QString& serviceName); + void serviceRemoved(const QString& serviceName); + +#ifdef QTM_BUILD_UNITTESTS + public: +#else + private: +#endif + enum TransactionType{Read, Write}; + + bool createTables(); + bool dropTables(); + bool checkTables(); + + bool checkConnection(); + + bool executeQuery(QSqlQuery *query, const QString &statement, const QList<QVariant> &bindValues = QList<QVariant>()); + QString getInterfaceID(QSqlQuery *query, const QServiceInterfaceDescriptor &interface); + bool insertInterfaceData(QSqlQuery *query, const QServiceInterfaceDescriptor &anInterface, const QString &serviceID); + + bool beginTransaction(QSqlQuery *query, TransactionType); + bool commitTransaction(QSqlQuery *query); + bool rollbackTransaction(QSqlQuery *query); + + bool populateInterfaceProperties(QServiceInterfaceDescriptor *descriptor, const QString &interfaceID); + bool populateServiceProperties(QServiceInterfaceDescriptor *descriptor, const QString &serviceID); + + QString m_databasePath; + QString m_connectionName; + bool m_isDatabaseOpen; + bool m_inTransaction; + DBError m_lastError; +}; + +QTM_END_NAMESPACE +QT_END_HEADER + +#endif /*QSERVICEDATABASE_H_*/ diff --git a/src/serviceframework/serviceframework.pro b/src/serviceframework/serviceframework.pro new file mode 100644 index 00000000..22489b1a --- /dev/null +++ b/src/serviceframework/serviceframework.pro @@ -0,0 +1,55 @@ +load(qt_module) +load(qt_module_config) + +TARGET = QtServiceFramework +QPRO_PWD = $PWD + +CONFIG += module +MODULE_PRI = ../../modules/qt_servicefrramework.pri + +QT = core sql + +DEFINES += QT_BUILD_SFW_LIB QT_MAKEDLL + +include(ipc/ipc.pri) + +PUBLIC_HEADERS += qservice.h \ + qservicemanager.h \ + qserviceplugininterface.h \ + qservicecontext.h \ + qabstractsecuritysession.h \ + qserviceinterfacedescriptor.h \ + qservicefilter.h \ + qremoteserviceregister.h +PRIVATE_HEADERS += servicedatabase_p.h \ + databasemanager_p.h \ + servicemetadata_p.h \ + qserviceinterfacedescriptor_p.h \ + dberror_p.h +SOURCES += servicemetadata.cpp \ + qservicemanager.cpp \ + qserviceplugininterface.cpp \ + qservicecontext.cpp \ + qabstractsecuritysession.cpp \ + qserviceinterfacedescriptor.cpp \ + qservicefilter.cpp \ + dberror.cpp \ + qremoteserviceregister.cpp +symbian { + contains(S60_VERSION, 5.2)|contains(MOBILITY_SD_MCL_BUILD, yes){ + DEFINES += SYMBIAN_EMULATOR_SUPPORTS_PERPROCESS_WSD + } + INCLUDEPATH += ./databasemanagerserver_symbian + PRIVATE_HEADERS += databasemanager_symbian_p.h + SOURCES += databasemanager_symbian.cpp + TARGET.CAPABILITY = ALL \ + -TCB + TARGET.UID3 = 0x2002AC84 + QtServiceFrameworkDeployment.sources = QtServiceFramework.dll \ + qsfwdatabasemanagerserver.exe + QtServiceFrameworkDeployment.path = /sys/bin + DEPLOYMENT += QtServiceFrameworkDeployment +} else:SOURCES += servicedatabase.cpp \ + databasemanager.cpp + +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS diff --git a/src/serviceframework/servicemetadata.cpp b/src/serviceframework/servicemetadata.cpp new file mode 100644 index 00000000..b0083e3a --- /dev/null +++ b/src/serviceframework/servicemetadata.cpp @@ -0,0 +1,725 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "servicemetadata_p.h" +#include <QFile> +#include <QDebug> +#include "qserviceinterfacedescriptor_p.h" + +//XML tags and attributes +//General +#define NAME_TAG "name" +#define DESCRIPTION_TAG "description" +#define SERVICEFW_TAG "SFW" +#define XML_MAX "1.1" + +//Service related +#define SERVICE_TAG "service" +#define SERVICE_FILEPATH "filepath" +#define SERVICE_IPCADDRESS "ipcaddress" + +//Interface related +#define INTERFACE_TAG "interface" +#define INTERFACE_VERSION "version" +#define INTERFACE_CAPABILITY "capabilities" +#define INTERFACE_CUSTOM_PROPERTY "customproperty" + +//Service type prefix +#define SERVICE_IPC_PREFIX "_q_ipc_addr:" + +QTM_BEGIN_NAMESPACE + +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &out, const ServiceMetaDataResults &r) +{ + out << r.type << r.name << r.location; + out << r.description << r.interfaces << r.latestInterfaces; + + return out; +} + +QDataStream &operator>>(QDataStream &in, ServiceMetaDataResults &r) +{ + in >> r.type >> r.name >> r.location; + in >> r.description >> r.interfaces >> r.latestInterfaces; + + return in; +} +#endif + +/* + \class ServiceMetaData + + \since 1.0 + + Utility class (used by service database) that offers support for + parsing metadata service xml registry file during service registration. \n + + It uses QXMLStreamReader class for parsing. Supproted Operations are: + - Parse the service and interfaces defined in XML file + - name, version, capabilitiesList, description and filePath of service can be retrieved + - each interface can be retrieved +*/ + +/* + * Class constructor + * + * @param aXmlFilePath path to the xml file that describes the service. + */ +ServiceMetaData::ServiceMetaData(const QString &aXmlFilePath) +{ + xmlDevice = new QFile(aXmlFilePath); + ownsXmlDevice = true; + latestError = 0; +} + +/* + * Class constructor + * + * @param device QIODevice that contains the XML data that describes the service. + */ +ServiceMetaData::ServiceMetaData(QIODevice *device) +{ + xmlDevice = device; + ownsXmlDevice = false; + latestError = 0; +} + +/* + * Class destructor + * + */ +ServiceMetaData::~ServiceMetaData() +{ + if (ownsXmlDevice) + delete xmlDevice; +} + +/* + Sets the device containing the XML data that describes the service to \a device. + */ +void ServiceMetaData::setDevice(QIODevice *device) +{ + clearMetadata(); + xmlDevice = device; + ownsXmlDevice = false; +} + +/* + Returns the device containing the XML data that describes the service. +*/ +QIODevice *ServiceMetaData::device() const +{ + return xmlDevice; +} + +/* + * Gets the service name + * + * @return service name or default value (empty string) if it is not available + */ +/*QString ServiceMetaData::name() const +{ + return serviceName; +}*/ + +/* + * Gets the path of the service implementation file + * + * @return service implementation filepath + */ +/*QString ServiceMetaData::location() const +{ + return serviceLocation; +}*/ + +/* + * Gets the service description + * + * @return service description or default value (empty string) if it is not available + */ +/*QString ServiceMetaData::description() const +{ + return serviceDescription; +}*/ + +/* + Returns the metadata of the interace at \a index; otherwise + returns 0. + */ +/*QList<QServiceInterfaceDescriptor> ServiceMetaData::getInterfaces() const +{ + return serviceInterfaces; +} */ + +/*! + \internal + + Returns a streamable object containing the results of the parsing. +*/ +ServiceMetaDataResults ServiceMetaData::parseResults() const +{ + ServiceMetaDataResults results; + results.type = serviceType; + results.location = serviceLocation; + results.name = serviceName; + results.description = serviceDescription; + results.interfaces = serviceInterfaces; + results.latestInterfaces = latestInterfaces(); + + return results; +} + +/* + Parses the file and extracts the service metadata \n + Custom error codes: \n + SFW_ERROR_UNABLE_TO_OPEN_FILE in case can not open the XML file \n + SFW_ERROR_INVALID_XML_FILE in case service registry is not a valid XML file \n + SFW_ERROR_NO_SERVICE in case XML file has no service tag\n + @return true if the metadata was read properly, false if there is an error + */ +bool ServiceMetaData::extractMetadata() +{ + Q_ASSERT(checkVersion(QLatin1String(XML_MAX))); + + latestError = 0; + clearMetadata(); + QXmlStreamReader xmlReader; + bool parseError = false; + //Open xml file + if (!xmlDevice->isOpen() && !xmlDevice->open(QIODevice::ReadOnly)) { + latestError = ServiceMetaData::SFW_ERROR_UNABLE_TO_OPEN_FILE; + parseError = true; + } else { + //Load xml content + xmlReader.setDevice(xmlDevice); + // Read XML doc + while (!xmlReader.atEnd() && !parseError) { + xmlReader.readNext(); + //Found <SFW> xml versioning tag introduced in 1.1 + //If this tag is not found the XML parser version will be 1.0 + if (xmlReader.isStartElement() && xmlReader.name() == QLatin1String(SERVICEFW_TAG)) { + if (!processVersionElement(xmlReader)) { + parseError = true; + } + } + //Found a <service> node, read service related metadata + else if (xmlReader.isStartElement() && xmlReader.name() == QLatin1String(SERVICE_TAG)) { + if (!processServiceElement(xmlReader)) { + parseError = true; + } + } + else if (xmlReader.isStartElement() && xmlReader.name() != QLatin1String(SERVICE_TAG) + && xmlReader.name() != QLatin1String(SERVICEFW_TAG)) { + latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE; + parseError = true; + } + else if (xmlReader.tokenType() == QXmlStreamReader::Invalid) { + latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; + parseError = true; + } + } + if (ownsXmlDevice) + xmlDevice->close(); + } + + if (parseError) { + //provide better error reports + switch (latestError) { + case SFW_ERROR_NO_SERVICE: /* Can not find service root node in XML file*/ + qDebug() << "Missing <service> tag"; + break; + case SFW_ERROR_NO_SERVICE_NAME: /* Can not find service name in XML file */ + qDebug() << "Missing or empty <name> tag within <service>"; + break; + case SFW_ERROR_NO_SERVICE_PATH: /* Can not find service filepath or ipcaddress in XML file */ + if (greaterThan(xmlVersion, QLatin1String("1.0"))) + qDebug() << "Missing or empty <filepath> or <ipcaddress> tag within <service>"; + else + qDebug() << "Missing or empty <filepath> tag within <service>"; + break; + case SFW_ERROR_NO_SERVICE_INTERFACE: /* No interface for the service in XML file*/ + qDebug() << "Missing <interface> tag"; + break; + case SFW_ERROR_NO_INTERFACE_VERSION: /* Can not find interface version in XML file */ + qDebug() << "Missing or empty <version> tag within <interface>"; + break; + case SFW_ERROR_NO_INTERFACE_NAME: /* Can not find interface name in XML file*/ + qDebug() << "Missing or empty <name> tag within <interface>"; + break; + case SFW_ERROR_UNABLE_TO_OPEN_FILE: /* Error opening XML file*/ + qDebug() << "Unable to open service xml file"; + break; + case SFW_ERROR_INVALID_XML_FILE: /* Not a valid XML file*/ + qDebug() << "Not a valid service xml"; + break; + case SFW_ERROR_PARSE_SERVICE: /* Error parsing service node */ + qDebug().nospace() << "Invalid tag within <service> with the supplied version(" + << xmlVersion << ")"; + break; + case SFW_ERROR_PARSE_INTERFACE: /* Error parsing interface node */ + qDebug() << "Invalid tag within <interface> tags"; + break; + case SFW_ERROR_DUPLICATED_INTERFACE: /* The same interface is defined twice */ + qDebug() << "The same interface has been defined more than once"; + break; + case SFW_ERROR_INVALID_VERSION: + qDebug() << "Invalid version string, expected: x.y"; + break; + case SFW_ERROR_DUPLICATED_TAG: /* The tag appears twice */ + qDebug() << "XML tag appears twice"; + break; + case SFW_ERROR_INVALID_CUSTOM_TAG: /* The customproperty tag is not correctly formatted or otherwise incorrect*/ + qDebug() << "Invalid custom property tag"; + break; + case SFW_ERROR_DUPLICATED_CUSTOM_KEY: /* The customproperty appears twice*/ + qDebug() << "Same custom property appears multiple times"; + break; + case SFW_ERROR_MULTIPLE_SERVICE_TYPES: /* Both filepath and ipcaddress found in the XML file */ + qDebug() << "Cannot specify both <filepath> and <ipcaddress> tags within <service>"; + break; + case SFW_ERROR_INVALID_FILEPATH: /* Service path cannot contain IPC prefix */ + qDebug() << "Invalid service location, avoid private prefixes"; + break; + case SFW_ERROR_INVALID_XML_VERSION: /* Error parsing servicefw version node */ + qDebug() << "Invalid or missing version attribute in <SFW> tag"; + break; + case SFW_ERROR_UNSUPPORTED_IPC: /* Servicefw version doesn't support IPC */ + qDebug().nospace() << "Supplied service framework version(" << xmlVersion + << ") doesn't support the <ipcaddress> tag"; + break; + case SFW_ERROR_UNSUPPORTED_XML_VERSION: /* Unsupported servicefw version supplied */ + qDebug().nospace() << "Service framework version(" << xmlVersion + << ") is higher than available support(" << QLatin1String(XML_MAX) << ")"; + break; + } + clearMetadata(); + } + return !parseError; +} + +/* + Gets the latest parsing error \n + @return parsing error(negative value) or 0 in case there is none + */ +int ServiceMetaData::getLatestError() const +{ + return latestError; +} + +/* + Parses the service framework xml version tag and continues to parse the service +*/ +bool ServiceMetaData::processVersionElement(QXmlStreamReader &aXMLReader) +{ + Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICEFW_TAG)); + bool parseError = false; + + if (aXMLReader.attributes().hasAttribute(QLatin1String("version"))) { + xmlVersion = aXMLReader.attributes().value(QLatin1String("version")).toString(); + bool success = checkVersion(xmlVersion); + + if (xmlVersion.isEmpty() || !success) { + latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_VERSION; + parseError = true; + } else { + if (greaterThan(xmlVersion, QLatin1String(XML_MAX))) { + latestError = ServiceMetaData::SFW_ERROR_UNSUPPORTED_XML_VERSION; + parseError = true; + } + } + } else { + latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_VERSION; + parseError = true; + } + + while (!parseError && !aXMLReader.atEnd()) { + aXMLReader.readNext(); + //Found a <service> node, read service related metadata + if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_TAG)) { + if (!processServiceElement(aXMLReader)) { + parseError = true; + } + } + else if (aXMLReader.isEndElement() && aXMLReader.name() == QLatin1String(SERVICEFW_TAG)) { + //Found </SFW>, leave the loop + break; + } + else if (aXMLReader.isStartElement() && aXMLReader.name() != QLatin1String(SERVICE_TAG)) { + latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE; + parseError = true; + } + else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { + latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; + parseError = true; + } + } + + return !parseError; +} + +/* + Parses and extracts the service metadata from the current xml <service> node \n + */ +bool ServiceMetaData::processServiceElement(QXmlStreamReader &aXMLReader) +{ + Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_TAG)); + bool parseError = false; + + int dupSTags[4] = {0 //->tag name + ,0 //-> service description + ,0 //-> filepath + ,0 //-> ipcaddress + }; + while (!parseError && !aXMLReader.atEnd()) { + aXMLReader.readNext(); + if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(NAME_TAG)) { + //Found <name> tag + serviceName = aXMLReader.readElementText(); + dupSTags[0]++; + } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(DESCRIPTION_TAG)) { + //Found <description> tag + serviceDescription = aXMLReader.readElementText(); + dupSTags[1]++; + } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_FILEPATH) ) { + //Found <filepath> tag for plugin service + dupSTags[2]++; + serviceLocation = aXMLReader.readElementText(); + //Check if IPC prefix was used incorrectly here + if (serviceLocation.startsWith(QLatin1String(SERVICE_IPC_PREFIX))) { + latestError = ServiceMetaData::SFW_ERROR_INVALID_FILEPATH; + parseError = true; + } + } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_IPCADDRESS) ) { + //Found <ipcaddress> tag for IPC service + //Check if servicefw XML version supports IPC + if (greaterThan(xmlVersion, QLatin1String("1.0"))) { + dupSTags[3]++; + serviceLocation = aXMLReader.readElementText(); + //Check if IPC prefix was used incorrectly here + if (serviceLocation.startsWith(QLatin1String(SERVICE_IPC_PREFIX))) { + latestError = ServiceMetaData::SFW_ERROR_INVALID_FILEPATH; + parseError = true; + } + } else { + latestError = ServiceMetaData::SFW_ERROR_UNSUPPORTED_IPC; + parseError = true; + } + } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_TAG)) { + //Found interface> node, read module related metadata + if (!processInterfaceElement(aXMLReader)) + parseError = true; + } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String("version")) { + //Found <version> tag on service level. We ignore this for now + aXMLReader.readElementText(); + } else if (aXMLReader.isEndElement() && aXMLReader.name() == QLatin1String(SERVICE_TAG)) { + //Found </service>, leave the loop + break; + } else if (aXMLReader.isEndElement() || aXMLReader.isStartElement()) { + latestError = ServiceMetaData::SFW_ERROR_PARSE_SERVICE; + parseError = true; + } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { + latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; + parseError = true; + } + } + + if ( !parseError ) { + if (serviceName.isEmpty()) { + latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_NAME; + parseError = true; + } else if (serviceLocation.isEmpty()) { + latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_PATH; + parseError = true; + } + } + + if (dupSTags[3] > 0) + serviceType = QService::InterProcess; + + if ((dupSTags[2] > 0) && (dupSTags[3] > 0)) { + latestError = SFW_ERROR_MULTIPLE_SERVICE_TYPES; + parseError = true; + } + + for (int i=0; !parseError && i<4; i++) { + if (dupSTags[i] > 1) { + latestError = SFW_ERROR_DUPLICATED_TAG; + parseError = true; + break; + } + } + + //update all interfaces with service data + const int icount = serviceInterfaces.count(); + if (icount == 0 && latestError == 0) { + latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_INTERFACE; + parseError = true; + } + for (int i = 0; i<icount; i++) { + serviceInterfaces.at(i).d->serviceName = serviceName; + serviceInterfaces.at(i).d->attributes[QServiceInterfaceDescriptor::Location] = serviceLocation; + serviceInterfaces.at(i).d->attributes[QServiceInterfaceDescriptor::ServiceDescription] = serviceDescription; + serviceInterfaces.at(i).d->attributes[QServiceInterfaceDescriptor::ServiceType] = serviceType; + } + + return !parseError; +} + +/* + Parses and extracts the interface metadata from the current xml <interface> node \n +*/ +bool ServiceMetaData::processInterfaceElement(QXmlStreamReader &aXMLReader) +{ + Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_TAG)); + bool parseError = false; + + //Read interface parameter + QString tmp; + QServiceInterfaceDescriptor aInterface; + int dupITags[4] = { + 0, //->iface name tag + 0, //->version + 0, //->capabilities + 0 //->description + }; + aInterface.d = new QServiceInterfaceDescriptorPrivate; + + while (!parseError && !aXMLReader.atEnd()) { + aXMLReader.readNext(); + //Read interface description + if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(NAME_TAG)) { + aInterface.d->interfaceName = aXMLReader.readElementText(); + dupITags[0]++; + //Found <name> tag for interface + } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(DESCRIPTION_TAG)) { + //Found <description> tag + aInterface.d->attributes[QServiceInterfaceDescriptor::InterfaceDescription] = aXMLReader.readElementText(); + dupITags[3]++; + //Found </interface>, leave the loop + } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_VERSION)) { + tmp.clear(); + tmp = aXMLReader.readElementText(); + if (tmp.isEmpty()) + continue; //creates NO_INTERFACE_VERSION error further below + bool success = checkVersion(tmp); + if ( success ) { + int majorVer = -1; + int minorVer = -1; + transformVersion(tmp, &majorVer, &minorVer); + aInterface.d->major = majorVer; + aInterface.d->minor = minorVer; + dupITags[1]++; + } else { + latestError = ServiceMetaData::SFW_ERROR_INVALID_VERSION; + parseError = true; + } + } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_CAPABILITY)) { + tmp.clear(); + tmp= aXMLReader.readElementText(); + aInterface.d->attributes[QServiceInterfaceDescriptor::Capabilities] = tmp.split(QLatin1String(","), QString::SkipEmptyParts); + dupITags[2]++; + } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_CUSTOM_PROPERTY)) { + parseError = true; + if (aXMLReader.attributes().hasAttribute(QLatin1String("key"))) { + const QString ref = aXMLReader.attributes().value(QLatin1String("key")).toString(); + if (!ref.isEmpty()) { + if (aInterface.d->customAttributes.contains(ref)) { + latestError = SFW_ERROR_DUPLICATED_CUSTOM_KEY; + continue; + } else { + QString value = aXMLReader.readElementText(); + if (value.isNull()) + value = QLatin1String(""); + aInterface.d->customAttributes[ref] = value; + parseError = false; + } + } + } + if (parseError) + latestError = SFW_ERROR_INVALID_CUSTOM_TAG; + } else if (aXMLReader.isEndElement() && aXMLReader.name() == QLatin1String(INTERFACE_TAG)) { + break; + } else if (aXMLReader.isStartElement() || aXMLReader.isEndElement()) { + latestError = ServiceMetaData::SFW_ERROR_PARSE_INTERFACE; + parseError = true; + } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { + latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; + parseError = true; + } + } + + if (!parseError) { + if (dupITags[1] == 0) { //no version tag found + latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_VERSION; + parseError = true; + } else if (aInterface.d->interfaceName.isEmpty()) { + latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_NAME; + parseError = true; + } + } + + for (int i=0;!parseError && i<4;i++) { + if (dupITags[i] > 1) { + parseError = true; + latestError = SFW_ERROR_DUPLICATED_TAG; + break; + } + } + + if (!parseError) { + const QString ident = aInterface.d->interfaceName + + QString::number(aInterface.majorVersion()) + + "." + + QString::number(aInterface.minorVersion()); + if (duplicates.contains(ident.toLower())) { + latestError = ServiceMetaData::SFW_ERROR_DUPLICATED_INTERFACE; + parseError = true; + } else { + duplicates.insert(ident.toLower()); + serviceInterfaces.append(aInterface); + if (!m_latestIndex.contains(aInterface.d->interfaceName.toLower()) + || lessThan(latestInterfaceVersion(aInterface.d->interfaceName), aInterface)) + + { + m_latestIndex[aInterface.d->interfaceName.toLower()] = serviceInterfaces.count() - 1; + } + } + } + return !parseError; +} + +QServiceInterfaceDescriptor ServiceMetaData::latestInterfaceVersion(const QString &interfaceName) +{ + QServiceInterfaceDescriptor ret; + if (m_latestIndex.contains(interfaceName.toLower())) + return serviceInterfaces[m_latestIndex[interfaceName.toLower()]]; + else + return ret; +} + +QList<QServiceInterfaceDescriptor> ServiceMetaData::latestInterfaces() const +{ + QList<QServiceInterfaceDescriptor> interfaces; + QHash<QString,int>::const_iterator i = m_latestIndex.constBegin(); + while (i != m_latestIndex.constEnd()) + { + interfaces.append(serviceInterfaces[i.value()]); + ++i; + } + return interfaces; +} + +bool ServiceMetaData::lessThan(const QServiceInterfaceDescriptor &d1, + const QServiceInterfaceDescriptor &d2) const +{ + return (d1.majorVersion() < d2.majorVersion()) + || ( d1.majorVersion() == d2.majorVersion() + && d1.minorVersion() < d2.minorVersion()); + +} + +bool ServiceMetaData::greaterThan(const QString &v1, const QString &v2) const +{ + int majorV1 = -1; + int minorV1 = -1; + transformVersion(v1, &majorV1, &minorV1); + + int majorV2 = -1; + int minorV2 = -1; + transformVersion(v2, &majorV2, &minorV2); + + return (majorV1 > majorV2 + || (majorV1 == majorV2 && minorV1 > minorV2)); +} + +bool ServiceMetaData::checkVersion(const QString &version) const +{ + //match x.y as version format + QRegExp rx(QLatin1String("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$")); + int pos = rx.indexIn(version); + QStringList list = rx.capturedTexts(); + bool success = false; + if (pos == 0 && list.count() == 3 + && rx.matchedLength() == version.length() ) + { + list[1].toInt(&success); + if ( success ) { + list[2].toInt(&success); + } + } + return success; +} + +void ServiceMetaData::transformVersion(const QString &version, int *major, int *minor) const +{ + Q_ASSERT(major != NULL); + Q_ASSERT(minor != NULL); + if (!checkVersion(version)) { + *major = -1; + *minor = -1; + } else { + QRegExp rx(QLatin1String("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$")); + rx.indexIn(version); + QStringList list = rx.capturedTexts(); + Q_ASSERT(list.count() == 3); + *major = list[1].toInt(); + *minor = list[2].toInt(); + } +} + + /* + * Clears the service metadata + * + */ +void ServiceMetaData::clearMetadata() +{ + xmlVersion = QLatin1String("1.0"); + serviceName.clear(); + serviceLocation.clear(); + serviceDescription.clear(); + serviceInterfaces.clear(); + duplicates.clear(); + m_latestIndex.clear(); + serviceType = QService::Plugin; +} + +QTM_END_NAMESPACE diff --git a/src/serviceframework/servicemetadata_p.h b/src/serviceframework/servicemetadata_p.h new file mode 100644 index 00000000..48d78dea --- /dev/null +++ b/src/serviceframework/servicemetadata_p.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SERVICEMETADATA_H +#define SERVICEMETADATA_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qserviceframeworkglobal.h" +#include <QXmlStreamReader> +#include <QStringList> +#include <QList> +#include <QSet> +#include "qserviceinterfacedescriptor.h" + +#ifdef SERVICE_XML_GENERATOR +#undef Q_AUTOTEST_EXPORT +#define Q_AUTOTEST_EXPORT +#endif + +QT_BEGIN_NAMESPACE +class QIODevice; + +// FORWARD DECLARATIONS +class QServiceInterfaceDescriptor; + +class ServiceMetaDataResults +{ +public: + ServiceMetaDataResults() {} + + ServiceMetaDataResults(const ServiceMetaDataResults& other) + { + type = other.type;; + location = other.location; + name = other.name; + description = other.description; + interfaces = other.interfaces; + latestInterfaces = other.latestInterfaces; + } + + int type; + QString location; + QString name; + QString description; + QList<QServiceInterfaceDescriptor> interfaces; + QList<QServiceInterfaceDescriptor> latestInterfaces; +}; + +#ifndef QT_NO_DATASTREAM +Q_AUTOTEST_EXPORT QDataStream &operator<<(QDataStream &, const ServiceMetaDataResults &); +Q_AUTOTEST_EXPORT QDataStream &operator>>(QDataStream &, ServiceMetaDataResults &); +#endif + +#ifdef IGNORE_SERVICEMETADATA_EXPORT + class ServiceMetaData +#else + class Q_AUTOTEST_EXPORT ServiceMetaData +#endif +{ +public: + + //! ServiceMetaData::ServiceMetadataErr + /*! + This enum describes the errors that may be returned by the Service metadata parser. + */ + enum ServiceMetadataErr { + SFW_ERROR_NO_SERVICE = 0, /* Can not find service root node in XML file*/ + SFW_ERROR_NO_SERVICE_NAME, /* Can not find service name in XML file */ + SFW_ERROR_NO_SERVICE_PATH, /* Can not find service filepath in XML file */ + SFW_ERROR_NO_SERVICE_INTERFACE, /* No interface for the service in XML file*/ + SFW_ERROR_NO_INTERFACE_VERSION, /* Can not find interface version in XML file */ + SFW_ERROR_NO_INTERFACE_NAME, /* Can not find interface name in XML file*/ + SFW_ERROR_UNABLE_TO_OPEN_FILE, /* Error opening XML file*/ + SFW_ERROR_INVALID_XML_FILE, /* Not a valid XML file*/ + SFW_ERROR_PARSE_SERVICE, /* Error parsing service node */ + SFW_ERROR_PARSE_INTERFACE, /* Error parsing interface node */ + SFW_ERROR_DUPLICATED_INTERFACE, /* The same interface is defined twice */ + SFW_ERROR_INVALID_VERSION, + SFW_ERROR_DUPLICATED_TAG, /* The tag appears twice */ + SFW_ERROR_INVALID_CUSTOM_TAG, /* The customproperty tag is not corectly formatted or otherwise incorrect*/ + SFW_ERROR_DUPLICATED_CUSTOM_KEY, /* The customproperty appears twice*/ + SFW_ERROR_MULTIPLE_SERVICE_TYPES, /* Both filepath and ipcaddress found in the XML file */ + SFW_ERROR_INVALID_FILEPATH, /* Service path cannot contain IPC prefix */ + SFW_ERROR_INVALID_XML_VERSION, /* Error parsing serficefw version node */ + SFW_ERROR_UNSUPPORTED_IPC, /* Servicefw version doesn't support IPC */ + SFW_ERROR_UNSUPPORTED_XML_VERSION /* Unsupported servicefw version supplied */ + }; + +public: + + ServiceMetaData(const QString &aXmlFilePath); + + ServiceMetaData(QIODevice *device); + + ~ServiceMetaData(); + + void setDevice(QIODevice *device); + + QIODevice *device() const; + + bool extractMetadata(); + + int getLatestError() const; + + ServiceMetaDataResults parseResults() const; + +private: + QList<QServiceInterfaceDescriptor> latestInterfaces() const; + QServiceInterfaceDescriptor latestInterfaceVersion(const QString &interfaceName); + bool processVersionElement(QXmlStreamReader &aXMLReader); + bool processServiceElement(QXmlStreamReader &aXMLReader); + bool processInterfaceElement(QXmlStreamReader &aXMLReader); + void clearMetadata(); + +private: + bool lessThan(const QServiceInterfaceDescriptor &d1, + const QServiceInterfaceDescriptor &d2) const; + bool greaterThan(const QString &v1, const QString &v2) const; + bool checkVersion(const QString &version) const; + void transformVersion(const QString &version, int *major, int *minor) const; + + QIODevice *xmlDevice; + bool ownsXmlDevice; + QString xmlVersion; + QString serviceName; + QString serviceLocation; + QString serviceDescription; + QService::Type serviceType; + QList<QServiceInterfaceDescriptor> serviceInterfaces; + QSet<QString> duplicates; + int latestError; + QHash<QString, int> m_latestIndex; +}; + +QT_END_NAMESPACE + +#endif // SERVICEMETADATA_H diff --git a/src/src.pro b/src/src.pro index f86b6d77..2d2a1182 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs CONFIG += ordered -SUBDIRS = systeminfo publishsubscribe imports +SUBDIRS = systeminfo publishsubscribe serviceframework imports |