Commit 8cf47d1f authored by spiiroin's avatar spiiroin

[sensormanager] Add plugin availability config and D-Bus queries. JB#41369

Allow disabling / enabling sensors plugins via config entries like:

  [available]
  proximitysensor=True
  magnetometersensor=False
  lidsensor=Feature_CoverSensor
  orientationsensor=Feature_GyroSensor|Feature_AccelerationSensor

Where:
- "True" means that loading of the sensor plugin is made available
  via D-Bus interface and can be loaded.
- "False" means the plugin will not be loaded and sensor is not
  made available via D-Bus
- "Feature_*" means that sensor availability is checked from hw
  settings. If sensorfwd is compiled without ssu-sysinfo support
  these will be treated similarly to "True".
- "" (or plugin that does not have config entry) is taken as "True",
  but a warning is logged in case of sensor plugins.

If a plugin that is enabled in configuration fails to load, it is
marked as not available until sensorfwd restart.

Add new D-Bus method calls:
- availablePlugins() lists all available plugins
- availableSensorPlugins() lists available sensor plugins (which,
  when loaded, make new sensor objects and interfaces available)
- pluginAvailable(name) can be used to check whether a named plugin
  is installed and available

Package default sensor availability configuration file that disables
all sensors except those that can be evaluated based on the hw
settings configuration.

Add example of device specific configuration file - these should be
installed from hw adaptation packages and can override the defaults.
Signed-off-by: spiiroin's avatarSimo Piiroinen <simo.piiroinen@jollamobile.com>
parent 4a04b3ed
[available]
; Availability of some sensors can be defined in hw-settings configuration
; files. By enabling ssu-sysinfo usage, sensorfwd configuration can be
; written so that it just refers to hw-settings config. If ssu-sysinfo
; is not used, these will default to "True".
;
; In case of virtual sensors can be implemented on top of multiple real
; sensors, multiple '|' separated features can be given and the sensor
; is enabled if at least one of those is set in hw-settings.
accelerometersensor=Feature_AccelerationSensor
alssensor=Feature_LightSensor
compasssensor=Feature_CompassSensor
gyroscopesensor=Feature_GyroSensor
orientationsensor=Feature_GyroSensor|Feature_AccelerationSensor
proximitysensor=Feature_ProximitySensor
; In theory having Feature_CoverSensor == have lidsensor. However
; in practice only mce is expected to track lidsensor and atm it
; does it via suspend proofed evdev inputs rather than sensorfwd.
; So, even if the lidsensor adaptors provided by sensorfwd would
; work, they might interfere with the only user of the sensor.
; lidsensor=Feature_CoverSensor
lidsensor=False
; To avoid revisiting config files for all old ports, the defaults
; added sensors should be set "False" by default here, and to "True"
; in device specific override config as appropriate.
humiditysensor=False
magnetometersensor=False
pressuresensor=False
rotationsensor=False
stepcountersensor=False
tapsensor=False
temperaturesensor=False
[available]
; Sensors that are disabled by default.
; -> Enable as appropriate
;humiditysensor=True
;magnetometersensor=True
;pressuresensor=True
;rotationsensor=True
;stepcountersensor=True
;tapsensor=True
;temperaturesensor=True
; Sensors that should/can be enabled/disabled based on
; hw settings config - or are enabled if sensorfwd is
; built without ssu-sysinfo support.
; -> Override as appropriate.
;accelerometersensor=False
;alssensor=False
;compasssensor=False
;gyroscopesensor=False
;lidsensor=False
;orientationsensor=False
;proximitysensor=False
......@@ -80,6 +80,11 @@ mce {
DEFINES += SENSORFW_MCE_WATCHER
}
contains(CONFIG,ssusysinfo) {
PKGCONFIG += ssu-sysinfo
QMAKE_CXXFLAGS += -DUSE_SSUSYSINFO
}
lunaservice {
SOURCES += lsclient.cpp
HEADERS += lsclient.h
......
......@@ -31,13 +31,19 @@
#include <QPluginLoader>
#include <QStringList>
#include <QList>
#include <QDir>
#include <QCoreApplication>
#include "logging.h"
#include "config.h"
#ifdef USE_SSUSYSINFO
# include <ssusysinfo/ssusysinfo.h>
#endif
Loader::Loader()
{
scanAvailablePlugins();
}
Loader& Loader::instance()
......@@ -47,99 +53,183 @@ Loader& Loader::instance()
return the_loader;
}
bool Loader::loadPluginFile(const QString& name, QString *errorString, QStringList& newPluginNames, QList<PluginBase*>& newPlugins) const
{
sensordLogT() << "Loading plugin:" << name;
#define PLUGIN_PREFIX_ENV "SENSORFW_LIBRARY_PATH"
#define PLUGIN_DIRECTORY "/usr/lib/sensord-qt5"
#define PLUGIN_PREFIX "lib"
#define PLUGIN_SUFFIX "-qt5.so"
#define SENSOR_SUFFIX "sensor"
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QString pluginPath = QString::fromLatin1("/usr/lib/sensord/lib%1.so").arg(name);
#else
QString pluginPath;
QByteArray env = qgetenv("SENSORFW_LIBRARY_PATH");
if (env.isEmpty())
pluginPath = QString::fromLatin1("/usr/lib/sensord-qt5/lib%1-qt5.so").arg(name);
else
pluginPath = QString::fromLatin1(env+"/usr/lib/sensord-qt5/lib%1-qt5.so").arg(name);
static QString getPluginDirectory()
{
QByteArray env = qgetenv(PLUGIN_PREFIX_ENV);
return QString::fromUtf8(env + PLUGIN_DIRECTORY);
}
#endif
static QString getPluginPath(const QString &name)
{
return QString("%1/" PLUGIN_PREFIX "%2" PLUGIN_SUFFIX).arg(getPluginDirectory()).arg(name);
}
QPluginLoader qpl(pluginPath);
bool Loader::loadPluginFile(const QString &name, QString &errorString, QStringList &stack)
{
const QString resolvedName(resolveRealPluginName(name));
QPluginLoader qpl(getPluginPath(resolvedName));
qpl.setLoadHints(QLibrary::ExportExternalSymbolsHint);
if (!qpl.load()) {
*errorString = qpl.errorString();
sensordLogC() << "plugin loading error: " << *errorString;
return false;
QObject *object = 0;
PluginBase *plugin = 0;
sensordLogD() << "Loader loading plugin:" << resolvedName << "as:" << name << "from:" << qpl.fileName();
bool loaded = false;
bool cyclic = stack.contains(resolvedName);
stack.prepend(resolvedName);
if (cyclic) {
errorString = "cyclic plugin dependency";
sensordLogC() << "Plugin has cyclic dependency:" << resolvedName;
} else if (loadedPluginNames_.contains(resolvedName)) {
sensordLogD() << "Plugin is already loaded:" << resolvedName;
loaded = true;
} else if (!pluginAvailable(resolvedName)) {
errorString = "plugin not available";
sensordLogW() << "Plugin not available:" << resolvedName;
} else if (!qpl.load()) {
errorString = qpl.errorString();
sensordLogC() << "Plugin loading error:" << resolvedName << "-" << errorString;
} else if (!(object = qpl.instance())) {
errorString = "not able to instanciate";
sensordLogC() << "Plugin loading error: " << resolvedName << "-" << errorString;
} else if (!(plugin = qobject_cast<PluginBase*>(object))) {
errorString = "not a Plugin type";
sensordLogC() << "Plugin loading error: " << resolvedName << "-" << errorString;
} else {
loaded = true;
QStringList dependencies(plugin->Dependencies());
sensordLogD() << resolvedName << "requires:" << dependencies;
foreach (const QString &dependency, dependencies) {
if (!(loaded = loadPluginFile(dependency, errorString, stack))) {
break;
}
}
if (loaded) {
plugin->Register(*this);
loadedPluginNames_.append(resolvedName);
plugin->Init(*this);
}
}
QObject* object = qpl.instance();
if (!object) {
*errorString = "not able to instanciate";
sensordLogC() << "plugin loading error: " << *errorString;
return false;
stack.removeOne(resolvedName);
if (!loaded) {
invalidatePlugin(resolvedName);
}
return loaded;
}
PluginBase* plugin = qobject_cast<PluginBase*>(object);
if (!plugin) {
*errorString = "not a Plugin type";
sensordLogC() << "plugin loading error: " << *errorString;
return false;
bool Loader::loadPlugin(const QString& name, QString *errorString)
{
QString error;
QStringList stack;
bool loaded = loadPluginFile(name, error, stack);
if (!loaded && errorString) {
*errorString = error;
}
return loaded;
}
// Add plugins to the front of the list so they are initialized in reverse order. This will guarantee that dependencies are initialized first for each plugin.
newPluginNames.prepend(name);
newPlugins.prepend(plugin);
// Get dependencies
QStringList requiredPlugins(plugin->Dependencies());
sensordLogT() << name << " requires: " << requiredPlugins;
bool loaded = true;
for (int i = 0; i < requiredPlugins.size() && loaded; ++i) {
if (!(loadedPluginNames_.contains(requiredPlugins.at(i)) ||
newPluginNames.contains(requiredPlugins.at(i))))
{
sensordLogT() << requiredPlugins.at(i) << " is not yet loaded, trying to load.";
QString resolvedName = resolveRealPluginName(requiredPlugins.at(i));
sensordLogT() << requiredPlugins.at(i) << " resolved as " << resolvedName << ". Loading";
loaded = loadPluginFile(resolvedName, errorString, newPluginNames, newPlugins);
#ifdef USE_SSUSYSINFO
static ssusysinfo_t *ssusysinfo = 0;
#endif
static bool evaluateAvailabilityValue(const QString &name, const QString &val)
{
bool available = true;
if (val.startsWith("Feature_")) {
#ifdef USE_SSUSYSINFO
const QStringList features(val.split("|"));
bool allow = false;
bool deny = false;
foreach(const QString &feature, features) {
hw_feature_t id = ssusysinfo_hw_feature_from_name(feature.toUtf8().constData());
if (id == Feature_Invalid ) {
sensordLogW() << "unknown hw feature:" << feature;
continue;
}
if( ssusysinfo_has_hw_feature(ssusysinfo, id) ) {
allow = true;
break;
}
deny = true;
}
if( deny && !allow ) {
sensordLogD() << "plugin disabled in hw-config: " << name << "value" << val;
available = false;
}
#else
// When compiled without ssu-support, these are enabled by design
sensordLogD() << "sensor plugin enabled implicitly: " << name << "value" << val;
#endif
} else if (val == "False") {
sensordLogD() << "plugin disabled sensorfwd config: " << name << "value" << val;
available = false;
} else if (name.endsWith(SENSOR_SUFFIX) && val != "True") {
// Warn about implicitly enabled sensor plugins
sensordLogW() << "sensor plugin enabled implicitly: " << name << "value" << val;
}
return loaded;
return available;
}
bool Loader::loadPlugin(const QString& name, QString* errorString)
void Loader::scanAvailablePlugins()
{
QString error;
bool loaded = false;
QStringList newPluginNames;
QList<PluginBase*> newPlugins;
if (loadedPluginNames_.contains(name)) {
sensordLogD() << "Plugin already loaded.";
return true;
#ifdef USE_SSUSYSINFO
if (!ssusysinfo) {
ssusysinfo = ssusysinfo_create();
}
#endif
QStringList res;
QDir dir(getPluginDirectory());
dir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDot | QDir::NoDotDot);
const QString prefix(PLUGIN_PREFIX);
const QString suffix(PLUGIN_SUFFIX);
foreach (QString file, dir.entryList()) {
if (file.startsWith(prefix) && file.endsWith(suffix)) {
int beg = prefix.size();
int end = file.size() - suffix.size();
const QString name(file.mid(beg, end-beg));
QString key = QString("available/%1").arg(name);
QString val = Config::configuration()->value(key).toString();
if( evaluateAvailabilityValue(name, val) ) {
res.append(name);
}
}
}
availablePluginNames_ = res;
#ifdef USE_SSUSYSINFO
ssusysinfo_delete(ssusysinfo), ssusysinfo = 0;
#endif
}
if (loadPluginFile(name, &error, newPluginNames, newPlugins)) {
QStringList Loader::availablePlugins() const
{
return availablePluginNames_;
}
// Register newly loaded plugins
foreach (PluginBase* base, newPlugins) {
base->Register(*this);
QStringList Loader::availableSensorPlugins() const
{
QStringList res;
foreach(const QString &name, availablePluginNames_) {
if (name.endsWith(SENSOR_SUFFIX)) {
res.append(name);
}
loadedPluginNames_.append(newPluginNames);
loaded = true;
}
return res;
}
// Init newly loaded plugins
foreach (PluginBase* base, newPlugins) {
base->Init(*this);
}
bool Loader::pluginAvailable(const QString &name) const
{
return availablePluginNames_.contains(name);
}
} else {
if(errorString)
*errorString = error;
loaded = false;
void Loader::invalidatePlugin(const QString &name)
{
if (availablePluginNames_.removeAll(name) > 0) {
sensordLogW() << "plugin marked invalid: " << name;
}
return loaded;
}
QString Loader::resolveRealPluginName(const QString& pluginName) const
......
......@@ -50,8 +50,31 @@ public:
* @param name plugin name.
* @param errorMessage object to write error message if plugin loading
* fails. If NULL then error message is not written.
* @return true on success, false on failure
*/
bool loadPlugin(const QString& name, QString* errorMessage = 0);
bool loadPlugin(const QString &name, QString *errorMessage = 0);
/**
* Test if a plugin is available for loading
*
* @param name plugin name
* @return true if plugin is available, false if not
*/
bool pluginAvailable(const QString &name) const;
/**
* Get a list of plugins available for loading
*
* @return Array of plugin names
*/
QStringList availablePlugins() const;
/**
* Get a list of sensor plugins available for loading
*
* @return Array of plugin names
*/
QStringList availableSensorPlugins() const;
private:
Loader();
......@@ -63,10 +86,11 @@ private:
*
* @param name plugin to load.
* @param errorString object to write error message if plugin loading fails.
* @param newPluginNames List of new loaded plugin names.
* @param newPlugin List of new loaded plugin objects.
* @param stack Pending plugin load stack for detecting circular dependencies.
*/
bool loadPluginFile(const QString& name, QString *errorString, QStringList& newPluginNames, QList<PluginBase*>& newPlugins) const;
bool loadPluginFile(const QString &name, QString &errorString, QStringList &stack);
void invalidatePlugin(const QString &name);
/**
* Resolve plugin name.
......@@ -74,9 +98,13 @@ private:
* @param pluginName plugin name.
* @return resolved plugin name.
*/
QString resolveRealPluginName(const QString& pluginName) const;
QString resolveRealPluginName(const QString &pluginName) const;
QStringList loadedPluginNames_; /**< list of loaded plugins */
QStringList availablePluginNames_; /**< list of loaded plugins */
void scanAvailablePlugins();
};
#endif
......@@ -333,6 +333,24 @@ bool SensorManager::loadPlugin(const QString& name)
return result;
}
QStringList SensorManager::availablePlugins() const
{
Loader& l = Loader::instance();
return l.availablePlugins();
}
bool SensorManager::pluginAvailable(const QString &name) const
{
Loader& l = Loader::instance();
return l.pluginAvailable(name);
}
QStringList SensorManager::availableSensorPlugins() const
{
Loader& l = Loader::instance();
return l.availableSensorPlugins();
}
int SensorManager::requestSensor(const QString& id)
{
sensordLogD() << "Requesting sensor:" << id;
......
......@@ -276,6 +276,27 @@ public:
*/
bool loadPlugin(const QString& name);
/**
* Test if a plugin is available
*
* @return true if plugin exists, false otherwise
*/
bool pluginAvailable(const QString &name) const;
/**
* List all available plugins.
*
* @return array of plugin names
*/
QStringList availablePlugins() const;
/**
* List available sensor plugins.
*
* @return array of plugin names
*/
QStringList availableSensorPlugins() const;
/**
* Request sensor.
*
......
......@@ -54,6 +54,21 @@ bool SensorManagerAdaptor::loadPlugin(const QString& name)
return sensorManager()->loadPlugin(name);
}
QStringList SensorManagerAdaptor::availablePlugins() const
{
return sensorManager()->availablePlugins();
}
bool SensorManagerAdaptor::pluginAvailable(const QString &name) const
{
return sensorManager()->pluginAvailable(name);
}
QStringList SensorManagerAdaptor::availableSensorPlugins() const
{
return sensorManager()->availableSensorPlugins();
}
int SensorManagerAdaptor::requestSensor(const QString &id, qint64 pid)
{
int session = sensorManager()->requestSensor(id);
......
......@@ -87,6 +87,27 @@ public Q_SLOTS:
*/
bool loadPlugin(const QString& name);
/**
* Test if a plugin is available
*
* @return true if plugin exists, false otherwise
*/
bool pluginAvailable(const QString& name) const;
/**
* List all available plugins.
*
* @return array of plugin names
*/
QStringList availablePlugins() const;
/**
* List available sensor plugins.
*
* @return array of plugin names
*/
QStringList availableSensorPlugins() const;
/**
* Request new sensor session to be created.
*
......
......@@ -21,6 +21,7 @@ BuildRequires: pkgconfig(Qt5Network)
BuildRequires: pkgconfig(Qt5Test)
BuildRequires: pkgconfig(mlite5)
BuildRequires: pkgconfig(libsystemd)
BuildRequires: pkgconfig(ssu-sysinfo)
BuildRequires: doxygen
BuildRequires: systemd
BuildRequires: libudev-devel
......@@ -94,6 +95,7 @@ export LD_RUN_PATH=/usr/lib/sensord-qt5/
export QT_SELECT=5
%qmake5 \
CONFIG+=ssusysinfo\
CONFIG+=mce
make %{?_smp_mflags}
......
......@@ -104,7 +104,8 @@ equals(QT_MAJOR_VERSION, 5): {
DBUSCONFIGFILES.path = /etc/dbus-1/system.d
INSTALLS += DBUSCONFIGFILES
SENSORDCONFIGFILES.files = config/10-sensord-default.conf
SENSORDCONFIGFILES.files = config/10-sensord-default.conf
SENSORDCONFIGFILES.files += config/20-sensors-default.conf
SENSORDCONFIGFILES.path = /etc/sensorfw/sensord.conf.d
INSTALLS += SENSORDCONFIGFILES
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment