Skip to content

Commit

Permalink
[sensormanager] Add plugin availability config and D-Bus queries. JB#…
Browse files Browse the repository at this point in the history
…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: Simo Piiroinen <simo.piiroinen@jollamobile.com>
  • Loading branch information
spiiroin committed May 29, 2018
1 parent 4a04b3e commit 8cf47d1
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 71 deletions.
36 changes: 36 additions & 0 deletions config/20-sensors-default.conf
@@ -0,0 +1,36 @@
[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
25 changes: 25 additions & 0 deletions config/60-sensor-selection-oeverrides.conf
@@ -0,0 +1,25 @@
[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
5 changes: 5 additions & 0 deletions core/core.pro
Expand Up @@ -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
Expand Down
226 changes: 158 additions & 68 deletions core/loader.cpp
Expand Up @@ -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()
Expand All @@ -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;
}

QObject* object = qpl.instance();
if (!object) {
*errorString = "not able to instanciate";
sensordLogC() << "plugin loading error: " << *errorString;
return false;
}

PluginBase* plugin = qobject_cast<PluginBase*>(object);
if (!plugin) {
*errorString = "not a Plugin type";
sensordLogC() << "plugin loading error: " << *errorString;
return false;
if (loaded) {
plugin->Register(*this);
loadedPluginNames_.append(resolvedName);
plugin->Init(*this);
}

// 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);
}
stack.removeOne(resolvedName);
if (!loaded) {
invalidatePlugin(resolvedName);
}
return loaded;
}

bool Loader::loadPlugin(const QString& name, QString *errorString)
{
QString error;
bool loaded = false;
QStringList newPluginNames;
QList<PluginBase*> newPlugins;
QStringList stack;
bool loaded = loadPluginFile(name, error, stack);
if (!loaded && errorString) {
*errorString = error;
}
return loaded;
}

#ifdef USE_SSUSYSINFO
static ssusysinfo_t *ssusysinfo = 0;
#endif

if (loadedPluginNames_.contains(name)) {
sensordLogD() << "Plugin already loaded.";
return true;
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 available;
}

if (loadPluginFile(name, &error, newPluginNames, newPlugins)) {
void Loader::scanAvailablePlugins()
{
#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
}

// Register newly loaded plugins
foreach (PluginBase* base, newPlugins) {
base->Register(*this);
QStringList Loader::availablePlugins() const
{
return availablePluginNames_;
}
loadedPluginNames_.append(newPluginNames);
loaded = true;

// Init newly loaded plugins
foreach (PluginBase* base, newPlugins) {
base->Init(*this);
QStringList Loader::availableSensorPlugins() const
{
QStringList res;
foreach(const QString &name, availablePluginNames_) {
if (name.endsWith(SENSOR_SUFFIX)) {
res.append(name);
}
}
return res;
}

} else {
if(errorString)
*errorString = error;
loaded = false;
bool Loader::pluginAvailable(const QString &name) const
{
return availablePluginNames_.contains(name);
}

return loaded;
void Loader::invalidatePlugin(const QString &name)
{
if (availablePluginNames_.removeAll(name) > 0) {
sensordLogW() << "plugin marked invalid: " << name;
}
}

QString Loader::resolveRealPluginName(const QString& pluginName) const
Expand Down

0 comments on commit 8cf47d1

Please sign in to comment.