aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGergely Risko <gergely+context@risko.hu>2009-09-16 16:46:22 +0300
committerGergely Risko <gergely+context@risko.hu>2009-09-16 16:46:22 +0300
commit7cf44bded328c27c6cbe18300ed379c4d706bc16 (patch)
tree72c7aaeb1d39365c11d9921303bdb8cc8a927fb0
parent23f77ad83b75a8a9256c6c72ca855ba1a358efd8 (diff)
parent3b6abe1576cc24652d91179161f0960ffb2c6ff3 (diff)
Merge commit 'origin/master' into cdbqvariant
-rw-r--r--configure.ac3
-rw-r--r--libcontextprovider/src/contextc.cpp4
-rw-r--r--libcontextprovider/src/service.cpp15
-rw-r--r--libcontextprovider/src/service.h2
-rw-r--r--libcontextprovider/src/servicebackend.cpp64
-rw-r--r--libcontextprovider/src/servicebackend.h2
-rw-r--r--libcontextprovider/unit-tests/service/servicebackend.h2
-rw-r--r--libcontextprovider/unit-tests/service/serviceunittest.cpp2
-rw-r--r--libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp8
-rw-r--r--libcontextsubscriber/cli/commandwatcher.cpp11
-rw-r--r--libcontextsubscriber/customer-tests/Makefile.am5
-rwxr-xr-xlibcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py41
-rw-r--r--libcontextsubscriber/customer-tests/common/cltool.py80
-rw-r--r--libcontextsubscriber/customer-tests/env.sh6
-rwxr-xr-xlibcontextsubscriber/customer-tests/pluginchanging/pluginchanging.py90
-rw-r--r--libcontextsubscriber/customer-tests/pluginchanging/time1.context.temp4
-rw-r--r--libcontextsubscriber/customer-tests/pluginchanging/time2.context.temp4
-rwxr-xr-xlibcontextsubscriber/customer-tests/runTests.sh7
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/Makefile.am2
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin.cpp68
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin.h59
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin1/.gitignore2
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am30
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin2/.gitignore2
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am30
-rw-r--r--libcontextsubscriber/src/Makefile.am6
-rw-r--r--libcontextsubscriber/src/contextpropertyinfo.cpp47
-rw-r--r--libcontextsubscriber/src/contextpropertyinfo.h8
-rw-r--r--libcontextsubscriber/src/iproviderplugin.h7
-rw-r--r--libcontextsubscriber/src/loggingfeatures.h1
-rw-r--r--libcontextsubscriber/src/propertyhandle.cpp13
-rw-r--r--libcontextsubscriber/src/provider.cpp52
-rw-r--r--libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h1
-rw-r--r--libcontextsubscriber/unit-tests/provider/Makefile.am4
-rw-r--r--spec/core.context20
35 files changed, 588 insertions, 114 deletions
diff --git a/configure.ac b/configure.ac
index 865567e6..ef97e23a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,6 +62,9 @@ AC_CONFIG_FILES([
libcontextsubscriber/contextsubscriber-1.0.pc
libcontextsubscriber/customer-tests/Makefile
libcontextsubscriber/customer-tests/update-contextkit-providers/Makefile
+ libcontextsubscriber/customer-tests/testplugins/Makefile
+ libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile
+ libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile
libcontextsubscriber/doc/Makefile
libcontextsubscriber/man/Makefile
libcontextsubscriber/src/Makefile
diff --git a/libcontextprovider/src/contextc.cpp b/libcontextprovider/src/contextc.cpp
index be98e5d7..394eb361 100644
--- a/libcontextprovider/src/contextc.cpp
+++ b/libcontextprovider/src/contextc.cpp
@@ -57,6 +57,10 @@ using namespace ContextProvider;
keys ("Battery.OnBattery", "Battery.ChargePercentage") and sets their
respective values.
+ Note: If the provider used other D-Bus bindings than QDBus, the
+ service name ("org.test.provider") needs to be unique, i.e., the
+ provider process should not register it itself.
+
\section Callbacks
The context_provider_install_key function and context_provider_install_group function
diff --git a/libcontextprovider/src/service.cpp b/libcontextprovider/src/service.cpp
index 0895a976..618e7c98 100644
--- a/libcontextprovider/src/service.cpp
+++ b/libcontextprovider/src/service.cpp
@@ -36,7 +36,7 @@ namespace ContextProvider {
This library implements the provider side of the Context Framework.
It has both a C++ and a C interface, so you can choose which you
- prefer.
+ prefer. For the documentation of the C API, see \ref CApi.
The C++ interface consists mainly of the three classes Service,
Property, and Group in the namespace ContextProvider. They are
@@ -233,12 +233,15 @@ void Service::setValue(const QString &key, const QVariant &val)
priv->backend->manager()->setKeyValue(key, val);
}
-/// Controls te service registration on dbus. If register service is set to
-/// true (by default) the service while be registered on dbus. Set to false
-// if you want to reuse an existing service (ie. provided by piece of code).
-void Service::setRegisterService(bool r)
+/// Set (override) the dbus \a connection to use by the Service. When the
+/// dbus connection is specified using this function the service is NOT
+/// automatically registered on the bus. It's ok to call this function many times
+/// as long as the connection name is the same all the time. It's NOT
+/// okay to call this function more than one time with connections with
+/// different names.
+void Service::setConnection(const QDBusConnection &connection)
{
- priv->backend->setRegisterService(r);
+ priv->backend->setConnection(connection);
}
/// Start the Service again after it has been stopped. All clients
diff --git a/libcontextprovider/src/service.h b/libcontextprovider/src/service.h
index 517b4e56..f5c5b5d1 100644
--- a/libcontextprovider/src/service.h
+++ b/libcontextprovider/src/service.h
@@ -53,7 +53,7 @@ public:
void setAsDefault();
void setValue(const QString &key, const QVariant &val);
- void setRegisterService(bool reg);
+ void setConnection(const QDBusConnection &connection);
private:
class ServicePrivate *priv;
diff --git a/libcontextprovider/src/servicebackend.cpp b/libcontextprovider/src/servicebackend.cpp
index d8004af5..8bbe93d7 100644
--- a/libcontextprovider/src/servicebackend.cpp
+++ b/libcontextprovider/src/servicebackend.cpp
@@ -47,8 +47,9 @@ struct ServiceBackendPrivate {
QString busName;
Manager *manager;
QDBusConnection *connection;
+ QDBusConnection *implicitConnection;
int refCount;
- bool registerService;
+ bool registeredService;
};
/// Creates new ServiceBackend. The backend automatically creates it's Manager.
@@ -63,8 +64,9 @@ ServiceBackend::ServiceBackend(QDBusConnection::BusType busType, const QString &
priv->busName = busName;
priv->manager = new Manager();
priv->connection = NULL;
+ priv->implicitConnection = NULL;
priv->refCount = 0;
- priv->registerService = true;
+ priv->registeredService = false;
}
/// Destroys the ServiceBackend. The backend is stopped.
@@ -74,6 +76,7 @@ ServiceBackend::~ServiceBackend()
{
contextDebug() << F_SERVICE_BACKEND << F_DESTROY << "Destroying Service";
stop();
+ delete priv->implicitConnection;
delete priv;
if (ServiceBackend::defaultServiceBackend == this)
ServiceBackend::defaultServiceBackend = NULL;
@@ -93,26 +96,29 @@ void ServiceBackend::setValue(const QString &key, const QVariant &val)
priv->manager->setKeyValue(key, val);
}
-/// Controls te service registration on dbus. If register service is set to
-/// true (by default) the service while be registered on dbus. Set to false
-/// if you want to reuse an existing service (ie. provided by piece of code).
-/// This function will fail if requested behavior is different from the current
-/// behavior and the service is already running.
-void ServiceBackend::setRegisterService(bool r)
+/// Set (override) the dbus \a connection to use by the ServiceBackend. When the
+/// dbus connection is specified using this function the service is NOT
+/// automatically registered on the bus. It's ok to call this function many times
+/// as long as the connection name is the same all the time. It's NOT
+/// okay to call this function more than one time with connections with
+/// different names.
+void ServiceBackend::setConnection(const QDBusConnection &connection)
{
- if (priv->registerService == r)
+ if (priv->connection && priv->connection->name() == connection.name()) {
+ // Silently exit
return;
-
- // Complain ONLY when actually trying to change value. This is important
- // for some black-box operation when you have no clue whetver you're starting
- // the service or just reusing it (ie. plugins).
+ }
if (priv->connection) {
- contextWarning() << F_SERVICE_BACKEND << "Trying to set service registration while service running";
+ contextWarning() << F_SERVICE_BACKEND << "Trying to change connection while service backed running!";
return;
- } else {
- priv->registerService = r;
}
+
+ if (priv->implicitConnection && connection.name() != priv->implicitConnection->name()) {
+ contextWarning() << F_SERVICE_BACKEND << "Connection was already set/forced with different name!";
+ return;
+ } else
+ priv->implicitConnection = new QDBusConnection(connection);
}
/// Start the ServiceBackend again after it has been stopped. All clients
@@ -123,15 +129,21 @@ bool ServiceBackend::start()
if (priv->connection)
return false;
- priv->connection = new QDBusConnection(QDBusConnection::connectToBus(priv->busType, priv->busName));
- ManagerAdaptor *managerAdaptor = new ManagerAdaptor(priv->manager, priv->connection);
+ if (priv->implicitConnection == NULL) {
+ priv->connection = new QDBusConnection(QDBusConnection::connectToBus(priv->busType, priv->busName));
- // Register service
- if (priv->registerService && !priv->connection->registerService(priv->busName)) {
- contextCritical() << F_SERVICE_BACKEND << "Failed to register service with name" << priv->busName;
- stop();
- return false;
- }
+ // Register service
+ if (!priv->connection->registerService(priv->busName)) {
+ contextCritical() << F_SERVICE_BACKEND << "Failed to register service with name" << priv->busName;
+ stop();
+ return false;
+ }
+
+ priv->registeredService = true;
+ } else
+ priv->connection = new QDBusConnection(*priv->implicitConnection);
+
+ ManagerAdaptor *managerAdaptor = new ManagerAdaptor(priv->manager, priv->connection);
// Register object
if (managerAdaptor && !priv->connection->registerObject("/org/freedesktop/ContextKit/Manager", priv->manager)) {
@@ -153,11 +165,13 @@ void ServiceBackend::stop()
// Unregister
priv->connection->unregisterObject("/org/freedesktop/ContextKit/Manager");
- priv->connection->unregisterService(priv->busName);
+ if (priv->registeredService)
+ priv->connection->unregisterService(priv->busName);
// Dealloc
delete priv->connection;
priv->connection = NULL;
+ priv->registeredService = false;
}
/// If the service is running, stop and start it again.
diff --git a/libcontextprovider/src/servicebackend.h b/libcontextprovider/src/servicebackend.h
index 1c1d6ba3..fc7cd02b 100644
--- a/libcontextprovider/src/servicebackend.h
+++ b/libcontextprovider/src/servicebackend.h
@@ -49,7 +49,7 @@ public:
void stop();
void restart();
- void setRegisterService(bool reg);
+ void setConnection(const QDBusConnection &connection);
void setAsDefault();
void setValue(const QString &key, const QVariant &val);
Manager *manager();
diff --git a/libcontextprovider/unit-tests/service/servicebackend.h b/libcontextprovider/unit-tests/service/servicebackend.h
index c11fc7da..c230ba0b 100644
--- a/libcontextprovider/unit-tests/service/servicebackend.h
+++ b/libcontextprovider/unit-tests/service/servicebackend.h
@@ -43,7 +43,7 @@ public:
static ServiceBackend *defaultService;
void setAsDefault();
- void setRegisterService(bool reg);
+ void setConnection(const QDBusConnection &connection);
void ref();
void unref();
diff --git a/libcontextprovider/unit-tests/service/serviceunittest.cpp b/libcontextprovider/unit-tests/service/serviceunittest.cpp
index fcf6dcba..59c69233 100644
--- a/libcontextprovider/unit-tests/service/serviceunittest.cpp
+++ b/libcontextprovider/unit-tests/service/serviceunittest.cpp
@@ -61,7 +61,7 @@ void ServiceBackend::setAsDefault()
defaultService = this;
}
-void ServiceBackend::setRegisterService(bool reg)
+void ServiceBackend::setConnection(const QDBusConnection &connection)
{
}
diff --git a/libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp b/libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp
index 9665cd94..66ba3574 100644
--- a/libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp
+++ b/libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp
@@ -33,7 +33,9 @@ struct ContextProvider::ServiceBackendPrivate {
QString busName;
Manager *manager;
QDBusConnection *connection;
+ QDBusConnection *implicitConnection;
int refCount;
+ bool registeredService;
};
QString *lastKey = NULL;
@@ -83,6 +85,7 @@ private slots:
void refCouting();
void manager();
void startAndStop();
+ void setConnection();
private:
ServiceBackend *serviceBackend;
@@ -160,6 +163,11 @@ void ServiceBackendUnitTest::setValue()
QCOMPARE(lastValue->toInt(), 99);
}
+void ServiceBackendUnitTest::setConnection()
+{
+ serviceBackend->setConnection(QDBusConnection::sessionBus());
+ QCOMPARE(serviceBackend->priv->implicitConnection->name(), QDBusConnection::sessionBus().name());
+}
#include "servicebackendunittest.moc"
QTEST_MAIN(ServiceBackendUnitTest);
diff --git a/libcontextsubscriber/cli/commandwatcher.cpp b/libcontextsubscriber/cli/commandwatcher.cpp
index a4841d96..c4b8d11e 100644
--- a/libcontextsubscriber/cli/commandwatcher.cpp
+++ b/libcontextsubscriber/cli/commandwatcher.cpp
@@ -17,7 +17,6 @@
CommandWatcher::CommandWatcher(int commandfd, QMap<QString, ContextProperty*> *properties, QObject *parent) :
QObject(parent), commandfd(commandfd), properties(properties)
{
- fcntl(commandfd, F_SETFL, O_NONBLOCK);
commandNotifier = new QSocketNotifier(commandfd, QSocketNotifier::Read, this);
sconnect(commandNotifier, SIGNAL(activated(int)), this, SLOT(onActivated()));
help();
@@ -29,6 +28,7 @@ void CommandWatcher::onActivated()
static QByteArray commandBuffer = "";
static char buf[1024];
int readSize;
+ fcntl(commandfd, F_SETFL, O_NONBLOCK);
while ((readSize = read(commandfd, &buf, 1024)) > 0)
commandBuffer += QByteArray(buf, readSize);
@@ -61,12 +61,14 @@ void CommandWatcher::help()
qDebug() << " type KEY - get the info()->type for a key";
qDebug() << " plugin KEY - get the info()->plugin for a key";
qDebug() << " constructionstring KEY - get the info()->constructionstring for a key";
+ qDebug() << " flush - write FLUSHED to stderr and stdout";
qDebug() << "Any prefix of a command can be used as an abbreviation";
}
void CommandWatcher::interpret(const QString& command) const
{
QTextStream out(stdout);
+ QTextStream err(stderr);
if (command == "") {
help();
} else {
@@ -74,7 +76,7 @@ void CommandWatcher::interpret(const QString& command) const
QString commandName = args[0];
args.pop_front();
- if (args.size() == 0) {
+ if (args.size() == 0 && !QString("flush").startsWith(commandName)) {
help();
return;
}
@@ -163,6 +165,11 @@ void CommandWatcher::interpret(const QString& command) const
out << "constructionstring: " << properties->value(key)->info()->constructionString() << endl;
else
qDebug() << "no such key:" << key;
+ } else if (QString("flush").startsWith(commandName)) {
+ out << "FLUSHED" << endl;
+ out.flush();
+ err << "FLUSHED" << endl;
+ err.flush();
} else
help();
}
diff --git a/libcontextsubscriber/customer-tests/Makefile.am b/libcontextsubscriber/customer-tests/Makefile.am
index 8eed733d..8ec2e565 100644
--- a/libcontextsubscriber/customer-tests/Makefile.am
+++ b/libcontextsubscriber/customer-tests/Makefile.am
@@ -1,4 +1,5 @@
-SUBDIRS = update-contextkit-providers
+SUBDIRS = update-contextkit-providers testplugins
+CHECKSUBDIRS = update-contextkit-providers
libcontextsubscribertestsdir = $(datadir)/libcontextsubscriber-tests
libcontextsubscribertests_DATA = tests.xml
@@ -6,7 +7,7 @@ libcontextsubscribertests_DATA = tests.xml
check-customer:
$(MAKE) -C $(top_srcdir) all
./runTests.sh
- for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done
+ for i in $(CHECKSUBDIRS); do $(MAKE) -C $$i $@; done
CLEANFILES = *.pyc
diff --git a/libcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py b/libcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py
index 11180b58..d1de94af 100755
--- a/libcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py
+++ b/libcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py
@@ -32,16 +32,9 @@ import os
import signal
import re
import time
-
import unittest
from subprocess import Popen, PIPE
-
-def timeoutHandler(signum, frame):
- raise Exception('tests has been running for too long')
-
-class Callable:
- def __init__(self, anycallable):
- self.__call__ = anycallable
+from cltool import CLTool
class Asynchronous(unittest.TestCase):
def startProvider(busname, args):
@@ -51,15 +44,7 @@ class Asynchronous(unittest.TestCase):
print >>ret.stdin, "info()"
ret.stdout.readline().rstrip()
return ret
- startProvider = Callable(startProvider)
-
- def wanted(name, type, value):
- return "%s = %s:%s" % (name, type, value)
- wanted = Callable(wanted)
-
- def wantedUnknown(name):
- return "%s is Unknown" % (name)
- wantedUnknown = Callable(wantedUnknown)
+ startProvider = staticmethod(startProvider)
#SetUp
def setUp(self):
@@ -97,20 +82,19 @@ class Asynchronous(unittest.TestCase):
"""
# check the fast property
- self.context_client = Popen(["context-listen", "test.fast", "test.slow"],
- stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ self.context_client = CLTool("context-listen", "test.fast", "test.slow")
- got = self.context_client.stdout.readline().rstrip()
- self.assertEqual(got,
- self.wanted("test.fast", "int", "42"),
- "Bad value for the fast property")
+ self.assert_(self.context_client.expect(CLTool.STDOUT,
+ CLTool.wanted("test.fast", "int", "42"),
+ 1), # timeout == 1 second
+ "Bad value for the fast property, wanted 42, communication:")
fast_time = time.time()
# check the slow property
- got = self.context_client.stdout.readline().rstrip()
- self.assertEqual(got,
- self.wanted("test.slow", "int", "42"),
- "Bad value for the slow property")
+ self.assert_(self.context_client.expect(CLTool.STDOUT,
+ CLTool.wanted("test.slow", "int", "42"),
+ 10), # timeout == 10 second max, but 3 is enough usually
+ "Bad value for the slow property, wanted 42, communication:")
slow_time = time.time()
self.assert_(slow_time - fast_time > 2.0,
@@ -129,9 +113,6 @@ def runTests():
result = unittest.TextTestRunner(verbosity=2).run(suiteInstallation)
return len(result.errors + result.failures)
-
if __name__ == "__main__":
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
- signal.signal(signal.SIGALRM, timeoutHandler)
- signal.alarm(10)
sys.exit(runTests())
diff --git a/libcontextsubscriber/customer-tests/common/cltool.py b/libcontextsubscriber/customer-tests/common/cltool.py
new file mode 100644
index 00000000..c80ce355
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/common/cltool.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+##
+## Copyright (C) 2008, 2009 Nokia. All rights reserved.
+##
+## Contact: Marius Vollmer <marius.vollmer@nokia.com>
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public License
+## version 2.1 as published by the Free Software Foundation.
+##
+## This library is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+## 02110-1301 USA
+
+import re
+import time
+from subprocess import Popen, PIPE
+
+class CLTool:
+ STDOUT = 1
+ STDERR = 2
+ def __init__(self, *cline):
+ self.__process = Popen(cline, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ self.__io = []
+
+ def send(self, string):
+ self.__io.append((0, string))
+ print >>self.__process.stdin, string
+
+ def expect(self, fileno, exp_str, timeout):
+ stream = 0
+ if fileno == self.STDOUT: stream = self.__process.stdout
+ if fileno == self.STDERR: stream = self.__process.stderr
+ if stream == 0: return False
+
+ print >>self.__process.stdin, "flush"
+ cur_str = ""
+ start_time = time.time()
+ while True:
+ line = stream.readline().rstrip()
+ if line == "FLUSHED":
+ if re.match(exp_str, cur_str):
+ return True
+ else:
+ time.sleep(0.1)
+ if time.time() - start_time > timeout:
+ self.printio()
+ print "Expected:", exp_str
+ print "Received before the timeout:\n", cur_str
+ return False
+ print >>self.__process.stdin, "flush"
+ else:
+ cur_str += line + "\n"
+ self.__io.append((fileno, line))
+
+ def printio(self):
+ print
+ print '----------------------------------------------------'
+ for line in self.__io:
+ if line[0] == 0:
+ print "[IN] <<<", line[1]
+ if line[0] == 1:
+ print "[OU] >>>", line[1]
+ if line[0] == 2:
+ print "[ER] >>>", line[1]
+ print '----------------------------------------------------'
+
+ def wanted(name, type, value):
+ return "%s = %s:%s$" % (name, type, value)
+ wanted = staticmethod(wanted)
+
+ def wantedUnknown(name):
+ return "%s is Unknown" % (name)
+ wantedUnknown = staticmethod(wantedUnknown)
diff --git a/libcontextsubscriber/customer-tests/env.sh b/libcontextsubscriber/customer-tests/env.sh
new file mode 100644
index 00000000..31a65d57
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/env.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+export PYTHONPATH="`pwd`/common/"
+export CONTEXT_PROVIDERS=.
+export LD_LIBRARY_PATH=../../src/.libs:../../../libcontextprovider/src/.libs
+export PATH=$PATH:../../../python:../../cli:../../reg-cli
diff --git a/libcontextsubscriber/customer-tests/pluginchanging/pluginchanging.py b/libcontextsubscriber/customer-tests/pluginchanging/pluginchanging.py
new file mode 100755
index 00000000..70a04149
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/pluginchanging/pluginchanging.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+##
+## This file is part of ContextKit.
+##
+## Copyright (C) 2009 Nokia. All rights reserved.
+##
+## Contact: Marius Vollmer <marius.vollmer@nokia.com>
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public License
+## version 2.1 as published by the Free Software Foundation.
+##
+## This library is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+## 02110-1301 USA
+##
+##
+## Requires python2.5-gobject and python2.5-dbus
+##
+import sys
+import unittest
+import os
+import string
+from subprocess import Popen, PIPE
+import time
+import signal
+
+def proc_kill(pid):
+ os.system('../common/rec-kill.sh %d' % pid)
+
+def timeoutHandler(signum, frame):
+ raise Exception('tests have been running for too long')
+
+def stdoutRead (object,lines):
+ list = []
+ for i in range(lines):
+ list.append(object.stdout.readline().rstrip())
+ return list
+
+class Subscription(unittest.TestCase):
+
+ def setUp(self):
+ os.environ["CONTEXT_PROVIDERS"] = "."
+ # We need 2 plugins which are in separate directories.
+ os.environ["CONTEXT_SUBSCRIBER_PLUGINS"] = "."
+ os.system('cp ../testplugins/timeplugin1/.libs/libcontextsubscribertime1.so* .')
+ os.system('cp ../testplugins/timeplugin2/.libs/libcontextsubscribertime2.so* .')
+
+
+ self.context_client = Popen(["context-listen","Test.Time"],stdin=PIPE,stdout=PIPE,stderr=PIPE)
+
+ def tearDown(self):
+ proc_kill(self.context_client.pid)
+ os.remove('time.context')
+ os.system('rm libcontextsubscribertime*.so*')
+
+ def testChangingPlugin(self):
+
+ # Copy the declaration file, declaring libcontextsubscribertime1 plugin.
+ os.system('cp time1.context.temp time.context.temp')
+ os.system('mv time.context.temp time.context')
+ #print "now reading"
+ actual = self.context_client.stdout.readline().rstrip()
+ #print actual
+
+ # The client got a value provided by the libcontextsubscribertime1
+ self.assertEqual(actual.startswith("Test.Time = QString:Time1: "), True, "Got: %s" % actual)
+
+ # Modify the registry so that the key is now provided by libcontextsubscribertime2
+ os.system('cp time2.context.temp time.context.temp')
+ os.system('mv time.context.temp time.context')
+
+ # Assert that the client starts getting the value from the correct plugin
+ # (not necessarily the first one)
+ actual = self.context_client.stdout.readline().rstrip()
+ actual = self.context_client.stdout.readline().rstrip()
+
+ self.assertEqual(actual.startswith("Test.Time = QString:Time2: "), True, "Got: %s" % actual)
+
+if __name__ == "__main__":
+ sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
+ signal.signal(signal.SIGALRM, timeoutHandler)
+ signal.alarm(30)
+ unittest.main()
diff --git a/libcontextsubscriber/customer-tests/pluginchanging/time1.context.temp b/libcontextsubscriber/customer-tests/pluginchanging/time1.context.temp
new file mode 100644
index 00000000..d29592fe
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/pluginchanging/time1.context.temp
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<provider xmlns="http://contextkit.freedesktop.org/Provider" plugin="/libcontextsubscribertime1" constructionString="time">
+ <key name="Test.Time"><type>STRING</type></key>
+</provider>
diff --git a/libcontextsubscriber/customer-tests/pluginchanging/time2.context.temp b/libcontextsubscriber/customer-tests/pluginchanging/time2.context.temp
new file mode 100644
index 00000000..c8a2757c
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/pluginchanging/time2.context.temp
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<provider xmlns="http://contextkit.freedesktop.org/Provider" plugin="/libcontextsubscribertime2" constructionString="time">
+ <key name="Test.Time"><type>STRING</type></key>
+</provider>
diff --git a/libcontextsubscriber/customer-tests/runTests.sh b/libcontextsubscriber/customer-tests/runTests.sh
index 82efc04f..c723a495 100755
--- a/libcontextsubscriber/customer-tests/runTests.sh
+++ b/libcontextsubscriber/customer-tests/runTests.sh
@@ -1,12 +1,11 @@
#!/bin/bash
-DIRS="commander subscription asynchronicity registry"
+cd $(dirname $0)
+DIRS="commander subscription asynchronicity registry pluginchanging"
+. ./env.sh
if pkg-config contextprovider-1.0 || [ -e ../../libcontextprovider/src/.libs/libcontextprovider.so ]
then
- export CONTEXT_PROVIDERS=.
- export LD_LIBRARY_PATH=../../src/.libs:../../../libcontextprovider/src/.libs
- export PATH=$PATH:../../../python:../../cli:../../reg-cli
for dir in $DIRS; do
cd $dir
diff --git a/libcontextsubscriber/customer-tests/testplugins/Makefile.am b/libcontextsubscriber/customer-tests/testplugins/Makefile.am
new file mode 100644
index 00000000..38b401c6
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = timeplugin1 timeplugin2
+EXTRA_DIST = timeplugin.cpp timeplugin.h
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin.cpp b/libcontextsubscriber/customer-tests/testplugins/timeplugin.cpp
new file mode 100644
index 00000000..6e3c4df0
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Contact: Marius Vollmer <marius.vollmer@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "timeplugin.h"
+#include "sconnect.h"
+
+#include "logging.h"
+
+#include <QDateTime>
+
+/// The factory method for constructing the IPropertyProvider instance.
+IProviderPlugin* pluginFactory(QString /*constructionString*/)
+{
+ // Note: it's the caller's responsibility to delete the plugin if
+ // needed.
+ return new ContextSubscriberTime::TimePlugin();
+}
+
+namespace ContextSubscriberTime {
+
+TimePlugin::TimePlugin()
+{
+ contextDebug();
+ prefix = TIME_PLUGIN_PREFIX;
+ timer.setInterval(2000);
+ sconnect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
+ QMetaObject::invokeMethod(this, "ready", Qt::QueuedConnection);
+}
+
+void TimePlugin::subscribe(QSet<QString> keys)
+{
+ contextDebug() << keys;
+ foreach(const QString& key, keys) {
+ emit subscribeFinished(key);
+ }
+ timer.start();
+}
+
+void TimePlugin::unsubscribe(QSet<QString> keys)
+{
+ timer.stop();
+}
+
+void TimePlugin::onTimeout()
+{
+ contextDebug() << "Timeout";
+ emit valueChanged("Test.Time", QDateTime::currentDateTime().toString().prepend(prefix));
+}
+
+} // end namespace
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin.h b/libcontextsubscriber/customer-tests/testplugins/timeplugin.h
new file mode 100644
index 00000000..3a51fb5a
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Contact: Marius Vollmer <marius.vollmer@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/*
+This is a test plugin for customer tests.
+*/
+
+#ifndef TIMEPLUGIN_H
+#define TIMEPLUGIN_H
+
+#include "iproviderplugin.h" // For IProviderPlugin definition
+#include <QTimer>
+
+using ContextSubscriber::IProviderPlugin;
+
+extern "C" {
+ IProviderPlugin* pluginFactory(QString constructionString);
+}
+
+namespace ContextSubscriberTime
+{
+
+class TimePlugin : public IProviderPlugin
+{
+ Q_OBJECT
+
+public:
+ explicit TimePlugin();
+ virtual void subscribe(QSet<QString> keys);
+ virtual void unsubscribe(QSet<QString> keys);
+
+private slots:
+ void onTimeout();
+
+private:
+ QTimer timer;
+ QString prefix;
+};
+}
+
+#endif
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin1/.gitignore b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/.gitignore
new file mode 100644
index 00000000..d79521dc
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/.gitignore
@@ -0,0 +1,2 @@
+timeplugin.cpp
+timeplugin.h
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am
new file mode 100644
index 00000000..723dfd06
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am
@@ -0,0 +1,30 @@
+lib_LTLIBRARIES = libcontextsubscribertime1.la
+libcontextsubscribertime1_la_SOURCES = timeplugin.cpp timeplugin.h
+
+BUILT_SOURCES = $(libcontextsubscribertime1_la_SOURCES)
+timeplugin.cpp: ../timeplugin.cpp
+ ln -sf $< $@
+timeplugin.h: ../timeplugin.h
+ ln -sf $< $@
+
+clean-local:
+ rm -f timeplugin.cpp timeplugin.h
+
+AM_CXXFLAGS = -I$(top_srcdir)/common \
+ -I$(srcdir)/../../../src $(QtCore_CFLAGS) \
+ $(QtDBus_CFLAGS) \
+ '-DCONTEXT_LOG_MODULE_NAME="time1plugin"' \
+ '-DTIME_PLUGIN_PREFIX="Time1: "'
+
+$(top_builddir)/common/libcommon.a:
+ $(MAKE) -C $(top_builddir)/common libcommon.a
+
+LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtDBus_LIBS)
+libcontextsubscribertime1_la_LIBADD=$(top_builddir)/common/libcommon.la
+
+.PHONY: $(top_builddir)/common/libcommon.la
+
+# moccing
+nodist_libcontextsubscribertime1_la_SOURCES = mocs.cpp
+QT_TOMOC = $(filter %.h, $(libcontextsubscribertime1_la_SOURCES))
+include $(top_srcdir)/am/qt.am
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin2/.gitignore b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/.gitignore
new file mode 100644
index 00000000..d79521dc
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/.gitignore
@@ -0,0 +1,2 @@
+timeplugin.cpp
+timeplugin.h
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am
new file mode 100644
index 00000000..6e1f27bc
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am
@@ -0,0 +1,30 @@
+lib_LTLIBRARIES = libcontextsubscribertime2.la
+libcontextsubscribertime2_la_SOURCES = timeplugin.cpp timeplugin.h
+
+BUILT_SOURCES = $(libcontextsubscribertime2_la_SOURCES)
+timeplugin.cpp: ../timeplugin.cpp
+ ln -sf $< $@
+timeplugin.h: ../timeplugin.h
+ ln -sf $< $@
+
+clean-local:
+ rm -f timeplugin.cpp timeplugin.h
+
+AM_CXXFLAGS = -I$(top_srcdir)/common \
+ -I$(srcdir)/../../../src $(QtCore_CFLAGS) \
+ $(QtDBus_CFLAGS) \
+ '-DCONTEXT_LOG_MODULE_NAME="time2plugin"' \
+ '-DTIME_PLUGIN_PREFIX="Time2: "'
+
+$(top_builddir)/common/libcommon.a:
+ $(MAKE) -C $(top_builddir)/common libcommon.a
+
+LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtDBus_LIBS)
+libcontextsubscribertime2_la_LIBADD=$(top_builddir)/common/libcommon.la
+
+.PHONY: $(top_builddir)/common/libcommon.la
+
+# moccing
+nodist_libcontextsubscribertime2_la_SOURCES = mocs.cpp
+QT_TOMOC = $(filter %.h, $(libcontextsubscribertime2_la_SOURCES))
+include $(top_srcdir)/am/qt.am
diff --git a/libcontextsubscriber/src/Makefile.am b/libcontextsubscriber/src/Makefile.am
index 051c7b92..b790c4dd 100644
--- a/libcontextsubscriber/src/Makefile.am
+++ b/libcontextsubscriber/src/Makefile.am
@@ -20,6 +20,7 @@ includecontextsubscriber_HEADERS = contextproperty.h \
AM_CXXFLAGS = -I$(top_srcdir)/common \
$(QtCore_CFLAGS) $(QtXml_CFLAGS) $(QtDBus_CFLAGS) \
+ '-DDEFAULT_CONTEXT_SUBSCRIBER_PLUGINS="@libdir@/contextkit/subscriber-plugins"' \
'-DDEFAULT_CONTEXT_PROVIDERS="@datadir@/contextkit/providers/"' \
'-DDEFAULT_CONTEXT_CORE_DECLARATIONS="@datadir@/contextkit/core.context"' \
'-DCONTEXT_LOG_MODULE_NAME="libcontextsubscriber"'
@@ -27,7 +28,7 @@ AM_CXXFLAGS = -I$(top_srcdir)/common \
$(top_builddir)/common/libcommon.la:
$(MAKE) -C $(top_builddir)/common libcommon.la
-LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtXml_LIBS) $(QtDBus_LIBS)
+LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtXml_LIBS) $(QtDBus_LIBS)
libcontextsubscriber_la_LIBADD=$(top_builddir)/common/libcommon.la
.PHONY: $(top_builddir)/common/libcommon.la
@@ -40,3 +41,6 @@ include $(top_srcdir)/am/qt.am
# because if you change configure parameter DEFAULT_CONTEXT_PROVIDERS,
# you should do a recompile
infocdbbackend.lo infoxmlbackend.lo: Makefile
+
+# and the same for DEFAULT_CONTEXT_SUBSCRIBER_PLUGINS
+provider.lo: Makefile
diff --git a/libcontextsubscriber/src/contextpropertyinfo.cpp b/libcontextsubscriber/src/contextpropertyinfo.cpp
index 1e23f2db..30cfd8b7 100644
--- a/libcontextsubscriber/src/contextpropertyinfo.cpp
+++ b/libcontextsubscriber/src/contextpropertyinfo.cpp
@@ -303,29 +303,48 @@ void ContextPropertyInfo::onKeyDataChanged(const QString& key)
if (key != keyName)
return;
+ // Update caches and store old values
+ QString oldType = cachedType;
QString newType = InfoBackend::instance()->typeForKey(keyName);
- if (cachedType != newType) {
+ cachedType = newType;
- if (cachedType == "")
+ cachedDoc = InfoBackend::instance()->docForKey(keyName);
+
+ QString oldPlugin = cachedPlugin;
+ QString oldConstructionString = cachedConstructionString;
+ QString newPlugin = InfoBackend::instance()->pluginForKey(keyName);
+ QString newConstructionString = InfoBackend::instance()->constructionStringForKey(keyName);
+ cachedPlugin = newPlugin;
+ cachedConstructionString = newConstructionString;
+
+ // Release the lock before emitting the signals; otherwise
+ // listeners trying to access cached values would create a
+ // deadlock.
+ lock.unlock();
+
+ // Emit the needed signals
+ if (oldType != newType) {
+
+ if (oldType == "")
emit existsChanged(true);
if (newType == "")
emit existsChanged(false);
- cachedType = newType;
emit typeChanged(cachedType);
}
- cachedDoc = InfoBackend::instance()->docForKey(keyName);
-
- // TBD: obsolete the providerChanged signal and add pluginChanged or sth?
- QString newPlugin = InfoBackend::instance()->pluginForKey(keyName);
- if (cachedPlugin == "contextkit-dbus" || newPlugin == "contextkit-dbus") {
- cachedPlugin = newPlugin;
- cachedConstructionString = InfoBackend::instance()->constructionStringForKey(keyName);
- QString newProvider = "";
- if (newPlugin == "contextkit-dbus") {
- newProvider = cachedConstructionString.split(":").last();
+ // TBD: obsolete the providerChanged & providerDBusTypeChanged signals?
+ if (oldPlugin != newPlugin || oldConstructionString != newConstructionString) {
+ if (oldPlugin == "contextkit-dbus" || newPlugin == "contextkit-dbus") {
+ QString newProvider = "";
+ if (newPlugin == "contextkit-dbus") {
+ newProvider = cachedConstructionString.split(":").last();
+ }
+ emit providerChanged(newProvider);
+ // Note: we don't emit providerDBusTypeChanged any
+ // more. It would be cumbersome, and there's no real use
+ // case for listening to it.
}
- emit providerChanged(newProvider);
+ emit pluginChanged(newPlugin, newConstructionString);
}
}
diff --git a/libcontextsubscriber/src/contextpropertyinfo.h b/libcontextsubscriber/src/contextpropertyinfo.h
index 3ba6b255..e83aebb9 100644
--- a/libcontextsubscriber/src/contextpropertyinfo.h
+++ b/libcontextsubscriber/src/contextpropertyinfo.h
@@ -85,6 +85,14 @@ signals:
/// signal you can wait (watch) for various keys to become available.
/// \param exists The new state of the key.
void existsChanged(bool exists);
+
+ /// Emitted when the libcontextsubscriber plugin providing the key
+ /// changes, or the construction parameter to give to the plugin
+ /// changes.. The \a plugin is the name of the new plugin
+ /// providing the key and the \a constructionString is the new
+ /// construction parameter to give to the plugin.
+ void pluginChanged(QString plugin, QString constructionString);
+
};
#endif // CONTEXTPROPERTYINFO_H
diff --git a/libcontextsubscriber/src/iproviderplugin.h b/libcontextsubscriber/src/iproviderplugin.h
index 2cfef845..dcd4342c 100644
--- a/libcontextsubscriber/src/iproviderplugin.h
+++ b/libcontextsubscriber/src/iproviderplugin.h
@@ -27,6 +27,11 @@
namespace ContextSubscriber {
+/* This is not a public API of ContextKit, please do not write third
+ * party plugins for the ContextKit client library without first
+ * contacting us.
+ */
+
class IProviderPlugin : public QObject
{
Q_OBJECT
@@ -42,6 +47,8 @@ signals:
void valueChanged(QString key, QVariant value);
};
+typedef IProviderPlugin* (*PluginFactoryFunc)(QString constructionString);
+
}
#endif
diff --git a/libcontextsubscriber/src/loggingfeatures.h b/libcontextsubscriber/src/loggingfeatures.h
index 86f71c25..05a0d276 100644
--- a/libcontextsubscriber/src/loggingfeatures.h
+++ b/libcontextsubscriber/src/loggingfeatures.h
@@ -26,5 +26,6 @@
#define F_XML (ContextFeature("xml"))
#define F_CDB (ContextFeature("cdb"))
#define F_DESTROY (ContextFeature("destroy"))
+#define F_PLUGINS (ContextFeature("plugins"))
#endif
diff --git a/libcontextsubscriber/src/propertyhandle.cpp b/libcontextsubscriber/src/propertyhandle.cpp
index f1592659..cef5c406 100644
--- a/libcontextsubscriber/src/propertyhandle.cpp
+++ b/libcontextsubscriber/src/propertyhandle.cpp
@@ -83,6 +83,12 @@ PropertyHandle::PropertyHandle(const QString& key)
// done before calling updateProvider.
myInfo = new ContextPropertyInfo(myKey, this);
+ // Start listening to changes in property introspection (e.g., added to registry, plugin changes)
+ sconnect(myInfo, SIGNAL(existsChanged(bool)),
+ this, SLOT(updateProvider()));
+ sconnect(myInfo, SIGNAL(pluginChanged(QString, QString)),
+ this, SLOT(updateProvider()));
+
// Start listening for the context commander, and also initiate a
// NameHasOwner check.
@@ -103,9 +109,6 @@ PropertyHandle::PropertyHandle(const QString& key)
// Otherwise, delay connecting to the provider until we know
// whether commander is present.
- // Start listening to changes in property registry (e.g., new keys, keys removed)
- sconnect(ContextRegistryInfo::instance(), SIGNAL(keysChanged(const QStringList&)),
- this, SLOT(updateProvider()));
// Move the PropertyHandle (and all children) to main thread.
moveToThread(QCoreApplication::instance()->thread());
@@ -127,6 +130,7 @@ void PropertyHandle::setTypeCheck(bool typeCheck)
void PropertyHandle::updateProvider()
{
Provider *newProvider;
+ contextDebug() << F_PLUGINS;
if (commandingEnabled && commanderListener->isServicePresent() == DBusNameListener::Present) {
// If commander is present it should be able to override the
@@ -139,6 +143,7 @@ void PropertyHandle::updateProvider()
if (myInfo->exists()) {
// If myInfo knows the current provider which should be
// connected to, connect to it.
+ contextDebug() << F_PLUGINS << "Key exists";
newProvider = Provider::instance(myInfo->plugin(),
myInfo->constructionString());
} else {
@@ -146,6 +151,8 @@ void PropertyHandle::updateProvider()
// This way, we can still continue communicating with the
// provider even though the key is no longer in the
// registry.
+ contextDebug() << F_PLUGINS << "Key doesn't exist -> keep old provider info";
+
newProvider = myProvider;
if (newProvider == 0) {
diff --git a/libcontextsubscriber/src/provider.cpp b/libcontextsubscriber/src/provider.cpp
index c5e8b931..dcf745fc 100644
--- a/libcontextsubscriber/src/provider.cpp
+++ b/libcontextsubscriber/src/provider.cpp
@@ -25,10 +25,12 @@
#include "sconnect.h"
#include "contextkitplugin.h"
#include "logging.h"
+#include "loggingfeatures.h"
#include <QTimer>
#include <QMutexLocker>
#include <QCoreApplication>
#include <QThread>
+#include <QLibrary>
namespace ContextSubscriber {
@@ -36,6 +38,11 @@ namespace ContextSubscriber {
\class IProviderPlugin
\brief Interface for provider plugins.
+ Note: this interface is private, currently it is not advised to use
+ it and create ContextKit subscriber plugins on your own, we can and
+ will change this interface anytime in the future even between small
+ bugfix releases.
+
Every Provider instance contains exactly one plugin (pointer) with
this interface which is constructed on initialization time and never
change after that. This way the concrete protocol (dbus, shared
@@ -45,7 +52,14 @@ namespace ContextSubscriber {
unsubscribe calls (on the wire) using the \c subscribe and \c
unsubscribe methods.
- The plugin can fail or became ready anytime because of things
+ When the plugin is constructed, it should emit the signal ready()
+ when it is ready to take in subscriptions. However, the signal
+ ready() should not be emitted in the plugin constructor. If the
+ plugin is able to take in subscriptions immediately, you can use
+ QMetaObject::invokeMethod with QueuedConnection to emit the signal
+ when the main loop is entered the next time.
+
+ The plugin can fail or became ready again anytime because of things
happening on the wire inside the plugin (socket closed, dbus service
appears/disappears). Whenever the plugin has new information about
this it should emit the signal \c ready or \c failed accordingly.
@@ -109,9 +123,43 @@ Provider::Provider(const QString &plugin, const QString &constructionString)
/// up with the name of the function).
void Provider::constructPlugin()
{
+ contextDebug() << F_PLUGINS;
if (pluginName == "contextkit-dbus") {
plugin = contextKitPluginFactory(constructionString);
- } else ; // FIXME: implement plugin system in the else branch
+ }
+ else if (pluginName.startsWith("/")) { // Require the plugin name to start with /
+ // Enable overriding the plugin location with an environment variable
+ const char *pluginPath = getenv("CONTEXT_SUBSCRIBER_PLUGINS");
+ if (! pluginPath)
+ pluginPath = DEFAULT_CONTEXT_SUBSCRIBER_PLUGINS;
+
+ QString pluginFilename(pluginPath);
+ // Allow pluginPath to have a trailing / or not
+ if (pluginFilename.endsWith("/")) {
+ pluginFilename.chop(1);
+ }
+
+ pluginFilename.append(pluginName);
+
+ QLibrary library(pluginFilename);
+ library.load();
+
+ if (library.isLoaded()) {
+ PluginFactoryFunc factory = (PluginFactoryFunc) library.resolve("pluginFactory");
+ if (factory) {
+ contextDebug() << "Resolved factory function";
+ plugin = factory(constructionString);
+ } else {
+ contextCritical() << "Error resolving function pluginFactory from plugin" << pluginFilename;
+ }
+ }
+ else {
+ contextCritical() << "Error loading plugin" << pluginFilename << ":" << library.errorString();
+ }
+ }
+ else {
+ contextCritical() << "Illegal plugin name" << pluginName << ", doesn't start with /";
+ }
if (plugin == 0) {
pluginState = FAILED;
diff --git a/libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h b/libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h
index a53f6d46..61cdcb45 100644
--- a/libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h
+++ b/libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h
@@ -50,6 +50,7 @@ signals:
void providerDBusTypeChanged(QDBusConnection::BusType newBusType);
void typeChanged(QString newType);
void existsChanged(bool exists);
+ void pluginChanged(QString, QString);
public:
// For the test program
diff --git a/libcontextsubscriber/unit-tests/provider/Makefile.am b/libcontextsubscriber/unit-tests/provider/Makefile.am
index 8d28ea53..936b9a92 100644
--- a/libcontextsubscriber/unit-tests/provider/Makefile.am
+++ b/libcontextsubscriber/unit-tests/provider/Makefile.am
@@ -9,7 +9,9 @@ COVERAGE_FILES = provider.cpp
# do the testing, coverage, etc. stuff
# tests.am is using +=, so we have to set a value here for these four always
-AM_CXXFLAGS = $(QtDBus_CFLAGS)
+AM_CXXFLAGS = $(QtDBus_CFLAGS) \
+ '-DDEFAULT_CONTEXT_SUBSCRIBER_PLUGINS="@libdir@/contextkit/subscriber-plugins"'
+
AM_LDFLAGS = $(QtDBus_LIBS)
# copy these files from the real source
FROM_SOURCE = provider.cpp provider.h iproviderplugin.h \
diff --git a/spec/core.context b/spec/core.context
index dccb80a3..f5569677 100644
--- a/spec/core.context
+++ b/spec/core.context
@@ -291,26 +291,6 @@ A boolean indicating whether or not the device is visible to other
Bluetooth devices when they search for others.
</doc>
</key>
- <key name="Bluetooth.TrafficOut">
- <type>percentage</type>
- <doc>
-A rough indication of the current outgoing traffic rate over
-Bluetooth, in percent of the maximum possible rate.
- </doc>
- </key>
- <key name="Bluetooth.TrafficIn">
- <type>percentage</type>
- <doc>
-A rough indication of the current incoming traffic rate over
-Bluetooth, in percent of the maximum possible rate.
- </doc>
- </key>
- <key name="Bluetooth.SignalStrength">
- <type>percentage</type>
- <doc>
-The strength of the Bluetooth radio connection.
- </doc>
- </key>
<doc>
Cellular
--------