diff options
author | Aaron McCarthy <mccarthy.aaron@gmail.com> | 2015-01-20 11:48:16 +1000 |
---|---|---|
committer | Aaron McCarthy <mccarthy.aaron@gmail.com> | 2015-09-10 07:46:11 +0000 |
commit | a3cb74a7ce3d4d138deb2e895288a36a1b3ce3ce (patch) | |
tree | ea69a2d563be4dee567be5bde8de0ddcadcf7074 /src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp | |
parent | f6a48c06d3090e553867db327665b3cc44d9b21a (diff) |
Remove dependency on libgeoclue.
Re-implement Geoclue plugin using only Qt DBus dropping both the
build-time and run-time dependency on libgeoclue. Allowing the Geoclue
plugin to be build on all platforms that support Qt DBus.
The priority of the Geoclue plugin has been lowered slightly so that
the native position plugin, if available, has precedence.
[ChangeLog][QtPositioning][Position] The Geoclue plugin has been
re-implemented using Qt DBus.
Task-number: QTBUG-40702
Change-Id: Ia06d089bfb46c10769ccffd765c044c361a9b484
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp')
-rw-r--r-- | src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp | 374 |
1 files changed, 161 insertions, 213 deletions
diff --git a/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp b/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp index fe5b048e..cc0a8119 100644 --- a/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp +++ b/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp @@ -33,7 +33,11 @@ ** ****************************************************************************/ -#include "qgeopositioninfosource_geocluemaster_p.h" +#include "qgeopositioninfosource_geocluemaster.h" + +#include <geoclue_interface.h> +#include <position_interface.h> +#include <velocity_interface.h> #include <QtCore/QDateTime> #include <QtCore/QFile> @@ -41,64 +45,23 @@ #include <QtCore/QStandardPaths> #include <QtCore/QVariantMap> #include <QtCore/QtNumeric> +#include <QtDBus/QDBusMetaType> #ifdef Q_LOCATION_GEOCLUE_DEBUG #include <QDebug> #endif -#include <dbus/dbus-glib.h> - #ifndef QT_NO_DATASTREAM #include <QtCore/QDataStream> #endif -QT_BEGIN_NAMESPACE - #define MINIMUM_UPDATE_INTERVAL 1000 #define UPDATE_TIMEOUT_COLD_START 120000 -namespace -{ - -void position_changed(GeocluePosition *position, GeocluePositionFields fields, int timestamp, - double latitude, double longitude, double altitude, - GeoclueAccuracy *accuracy, QGeoPositionInfoSourceGeoclueMaster *master) -{ - Q_UNUSED(position) - - if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE && fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) - master->updatePosition(fields, timestamp, latitude, longitude, altitude, accuracy); - else - master->regularUpdateFailed(); -} - -void velocity_changed(GeoclueVelocity *velocity, GeoclueVelocityFields fields, int timestamp, - double speed, double direction, double climb, - QGeoPositionInfoSourceGeoclueMaster *master) -{ - Q_UNUSED(velocity) - - if (fields == GEOCLUE_VELOCITY_FIELDS_NONE) - master->velocityUpdateFailed(); - else - master->velocityUpdateSucceeded(fields, timestamp, speed, direction, climb); -} +QT_BEGIN_NAMESPACE -void position_callback(GeocluePosition *pos, GeocluePositionFields fields, int timestamp, - double latitude, double longitude, double altitude, - GeoclueAccuracy *accuracy, GError *error, gpointer userdata) +namespace { - Q_UNUSED(pos) - - if (error) - g_error_free(error); - - QGeoPositionInfoSourceGeoclueMaster *master = - static_cast<QGeoPositionInfoSourceGeoclueMaster *>(userdata); - - if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE && fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) - master->updatePosition(fields, timestamp, latitude, longitude, altitude, accuracy); -} double knotsToMetersPerSecond(double knots) { @@ -108,11 +71,13 @@ double knotsToMetersPerSecond(double knots) } QGeoPositionInfoSourceGeoclueMaster::QGeoPositionInfoSourceGeoclueMaster(QObject *parent) -: QGeoPositionInfoSource(parent), QGeoclueMaster(this), m_pos(0), m_vel(0), - m_lastVelocityIsFresh(false), m_regularUpdateTimedOut(false), m_lastVelocity(qQNaN()), +: QGeoPositionInfoSource(parent), m_master(new QGeoclueMaster(this)), m_provider(0), m_pos(0), + m_vel(0), m_lastVelocityIsFresh(false), m_regularUpdateTimedOut(false), m_lastVelocity(qQNaN()), m_lastDirection(qQNaN()), m_lastClimb(qQNaN()), m_lastPositionFromSatellite(false), - m_methods(AllPositioningMethods), m_running(false), m_error(NoError) + m_running(false), m_error(NoError) { + qDBusRegisterMetaType<Accuracy>(); + #ifndef QT_NO_DATASTREAM // Load the last known location QFile file(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + @@ -123,8 +88,11 @@ QGeoPositionInfoSourceGeoclueMaster::QGeoPositionInfoSourceGeoclueMaster(QObject } #endif + connect(m_master, SIGNAL(positionProviderChanged(QString,QString,QString,QString)), + this, SLOT(positionProviderChanged(QString,QString,QString,QString))); + m_requestTimer.setSingleShot(true); - QObject::connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestUpdateTimeout())); + connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestUpdateTimeout())); setPreferredPositioningMethods(AllPositioningMethods); } @@ -144,50 +112,10 @@ QGeoPositionInfoSourceGeoclueMaster::~QGeoPositionInfoSourceGeoclueMaster() } #endif - if (m_pos) - g_object_unref (m_pos); - if (m_vel) - g_object_unref(m_vel); -} - -void QGeoPositionInfoSourceGeoclueMaster::velocityUpdateFailed() -{ -#ifdef Q_LOCATION_GEOCLUE_DEBUG - qDebug() << "QGeoPositionInfoSourceGeoclueMaster velocity update failed."; -#endif - // Set the velocitydata non-fresh. - m_lastVelocityIsFresh = false; -} - -void QGeoPositionInfoSourceGeoclueMaster::velocityUpdateSucceeded(GeoclueVelocityFields fields, - int timestamp, double speed, - double direction, double climb) -{ - Q_UNUSED(timestamp); - -#ifdef Q_LOCATION_GEOCLUE_DEBUG - qDebug() << "QGeoPositionInfoSourceGeoclueMaster velocity update succeeded, speed: " << speed; -#endif - // Store the velocity and mark it as fresh. Simple but hopefully adequate. - if (fields & GEOCLUE_VELOCITY_FIELDS_SPEED) - m_lastVelocity = knotsToMetersPerSecond(speed); - else - m_lastVelocity = qQNaN(); - - if (fields & GEOCLUE_VELOCITY_FIELDS_DIRECTION) - m_lastDirection = direction; - else - m_lastDirection = qQNaN(); - - if (fields & GEOCLUE_VELOCITY_FIELDS_CLIMB) - m_lastClimb = climb; - else - m_lastClimb = qQNaN(); - - m_lastVelocityIsFresh = true; + cleanupPositionSource(); } -void QGeoPositionInfoSourceGeoclueMaster::regularUpdateFailed() +void QGeoPositionInfoSourceGeoclueMaster::positionUpdateFailed() { #ifdef Q_LOCATION_GEOCLUE_DEBUG qDebug() << "QGeoPositionInfoSourceGeoclueMaster regular update failed."; @@ -200,33 +128,25 @@ void QGeoPositionInfoSourceGeoclueMaster::regularUpdateFailed() } } -void QGeoPositionInfoSourceGeoclueMaster::updatePosition(GeocluePositionFields fields, - int timestamp, double latitude, - double longitude, double altitude, - GeoclueAccuracy *accuracy) +void QGeoPositionInfoSourceGeoclueMaster::updatePosition(PositionFields fields, int timestamp, + double latitude, double longitude, + double altitude, Accuracy accuracy) { if (m_requestTimer.isActive()) m_requestTimer.stop(); QGeoCoordinate coordinate(latitude, longitude); - if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) + if (fields & Altitude) coordinate.setAltitude(altitude); m_lastPosition = QGeoPositionInfo(coordinate, QDateTime::fromTime_t(timestamp)); - if (accuracy) { - double horizontalAccuracy = qQNaN(); - double verticalAccuracy = qQNaN(); - GeoclueAccuracyLevel accuracyLevel = GEOCLUE_ACCURACY_LEVEL_NONE; - geoclue_accuracy_get_details(accuracy, &accuracyLevel, &horizontalAccuracy, &verticalAccuracy); + m_lastPositionFromSatellite = accuracy.level() == Accuracy::Detailed; - m_lastPositionFromSatellite = accuracyLevel & GEOCLUE_ACCURACY_LEVEL_DETAILED; - - if (!qIsNaN(horizontalAccuracy)) - m_lastPosition.setAttribute(QGeoPositionInfo::HorizontalAccuracy, horizontalAccuracy); - if (!qIsNaN(verticalAccuracy)) - m_lastPosition.setAttribute(QGeoPositionInfo::VerticalAccuracy, verticalAccuracy); - } + if (!qIsNaN(accuracy.horizontal())) + m_lastPosition.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy.horizontal()); + if (!qIsNaN(accuracy.vertical())) + m_lastPosition.setAttribute(QGeoPositionInfo::VerticalAccuracy, accuracy.vertical()); if (m_lastVelocityIsFresh) { if (!qIsNaN(m_lastVelocity)) @@ -253,69 +173,50 @@ void QGeoPositionInfoSourceGeoclueMaster::updatePosition(GeocluePositionFields f // Only stop positioning if regular updates not active. if (!m_running) { cleanupPositionSource(); - releaseMasterClient(); + m_master->releaseMasterClient(); } } +void QGeoPositionInfoSourceGeoclueMaster::velocityUpdateFailed() +{ + // Set the velocitydata non-fresh. + m_lastVelocityIsFresh = false; +} + +void QGeoPositionInfoSourceGeoclueMaster::updateVelocity(VelocityFields fields, int timestamp, + double speed, double direction, + double climb) +{ + Q_UNUSED(timestamp) + + // Store the velocity and mark it as fresh. Simple but hopefully adequate. + m_lastVelocity = (fields & Speed) ? knotsToMetersPerSecond(speed) : qQNaN(); + m_lastDirection = (fields & Direction) ? direction : qQNaN(); + m_lastClimb = (fields & Climb) ? climb : qQNaN(); + m_lastVelocityIsFresh = true; +} + void QGeoPositionInfoSourceGeoclueMaster::cleanupPositionSource() { - if (m_pos) { - g_object_unref(m_pos); - m_pos = 0; - } - if (m_vel) { - g_object_unref(m_vel); - m_vel = 0; - } + if (m_provider) + m_provider->RemoveReference(); + delete m_provider; + m_provider = 0; + delete m_pos; + m_pos = 0; + delete m_vel; + m_vel = 0; } void QGeoPositionInfoSourceGeoclueMaster::setOptions() { - if (!m_pos) + if (!m_provider) return; QVariantMap options; options.insert(QStringLiteral("UpdateInterval"), updateInterval()); - GHashTable *gOptions = g_hash_table_new(g_str_hash, g_str_equal); - - for (QVariantMap::ConstIterator i = options.constBegin(); i != options.constEnd(); ++i) { - char *key = qstrdup(i.key().toUtf8().constData()); - - const QVariant v = i.value(); - - GValue *value = new GValue; - memset(value, 0, sizeof(*value)); - - switch (v.userType()) { - case QMetaType::QString: - g_value_init(value, G_TYPE_STRING); - g_value_set_string(value, v.toString().toUtf8().constData()); - break; - case QMetaType::Int: - g_value_init(value, G_TYPE_INT); - g_value_set_int(value, v.toInt()); - break; - default: - qWarning("Unexpected type %d %s", v.userType(), v.typeName()); - } - - g_hash_table_insert(gOptions, key, value); - } - - geoclue_provider_set_options(GEOCLUE_PROVIDER(m_pos), gOptions, 0); - - GHashTableIter iter; - char *key; - GValue *value; - - g_hash_table_iter_init(&iter, gOptions); - while (g_hash_table_iter_next(&iter, reinterpret_cast<void **>(&key), reinterpret_cast<void **>(&value))) { - delete[] key; - delete value; - } - - g_hash_table_destroy(gOptions); + m_provider->SetOptions(options); } void QGeoPositionInfoSourceGeoclueMaster::setUpdateInterval(int msec) @@ -341,13 +242,13 @@ void QGeoPositionInfoSourceGeoclueMaster::setPreferredPositioningMethods(Positio // Don't start Geoclue provider until necessary. Don't currently have a master client, no need // no recreate one. - if (!hasMasterClient()) + if (!m_master->hasMasterClient()) return; // Free potential previous sources, because new requirements can't be set for the client // (creating a position object after changing requirements seems to fail). cleanupPositionSource(); - releaseMasterClient(); + m_master->releaseMasterClient(); // Restart Geoclue provider with new requirements. configurePositionSource(); @@ -356,18 +257,14 @@ void QGeoPositionInfoSourceGeoclueMaster::setPreferredPositioningMethods(Positio QGeoPositionInfo QGeoPositionInfoSourceGeoclueMaster::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const { - if (fromSatellitePositioningMethodsOnly) { - if (m_lastPositionFromSatellite) - return m_lastPosition; - else - return QGeoPositionInfo(); - } + if (fromSatellitePositioningMethodsOnly && !m_lastPositionFromSatellite) + return QGeoPositionInfo(); + return m_lastPosition; } QGeoPositionInfoSourceGeoclueMaster::PositioningMethods QGeoPositionInfoSourceGeoclueMaster::supportedPositioningMethods() const { - // There is no really knowing which methods the GeoClue master supports. return AllPositioningMethods; } @@ -383,7 +280,7 @@ void QGeoPositionInfoSourceGeoclueMaster::startUpdates() m_running = true; // Start Geoclue provider. - if (!hasMasterClient()) { + if (!m_master->hasMasterClient()) { configurePositionSource(); setOptions(); } @@ -405,17 +302,22 @@ void QGeoPositionInfoSourceGeoclueMaster::stopUpdates() if (!m_running) return; - if (m_pos) - g_signal_handlers_disconnect_by_func(G_OBJECT(m_pos), (void *)position_changed, this); - if (m_vel) - g_signal_handlers_disconnect_by_func(G_OBJECT(m_vel), (void *)velocity_changed, this); + if (m_pos) { + disconnect(m_pos, SIGNAL(PositionChanged(qint32,qint32,double,double,double,Accuracy)), + this, SLOT(positionChanged(qint32,qint32,double,double,double,Accuracy))); + } + + if (m_vel) { + disconnect(m_vel, SIGNAL(VelocityChanged(qint32,qint32,double,double,double)), + this, SLOT(velocityChanged(qint32,qint32,double,double,double))); + } m_running = false; // Only stop positioning if single update not requested. if (!m_requestTimer.isActive()) { cleanupPositionSource(); - releaseMasterClient(); + m_master->releaseMasterClient(); } } @@ -432,7 +334,7 @@ void QGeoPositionInfoSourceGeoclueMaster::requestUpdate(int timeout) return; } - if (!hasMasterClient()) { + if (!m_master->hasMasterClient()) { configurePositionSource(); setOptions(); } @@ -442,8 +344,55 @@ void QGeoPositionInfoSourceGeoclueMaster::requestUpdate(int timeout) // for whole cold start time. m_requestTimer.start(timeout ? timeout : UPDATE_TIMEOUT_COLD_START); - if (m_pos) - geoclue_position_get_position_async(m_pos, position_callback, this); + if (m_pos) { + QDBusPendingReply<qint32, qint32, double, double, double, Accuracy> reply = m_pos->GetPosition(); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(getPositionFinished(QDBusPendingCallWatcher*))); + } +} + +void QGeoPositionInfoSourceGeoclueMaster::positionProviderChanged(const QString &name, + const QString &description, + const QString &service, + const QString &path) +{ + Q_UNUSED(name) + Q_UNUSED(description) + + cleanupPositionSource(); + + if (service.isEmpty() || path.isEmpty()) { + if (!m_regularUpdateTimedOut) { + m_regularUpdateTimedOut = true; + emit updateTimeout(); + } + return; + } + + m_provider = new OrgFreedesktopGeoclueInterface(service, path, QDBusConnection::sessionBus()); + m_provider->AddReference(); + + m_pos = new OrgFreedesktopGeocluePositionInterface(service, path, QDBusConnection::sessionBus()); + + if (m_running) { + connect(m_pos, SIGNAL(PositionChanged(qint32,qint32,double,double,double,Accuracy)), + this, SLOT(positionChanged(qint32,qint32,double,double,double,Accuracy))); + } + + // Get the current position immediately. + QDBusPendingReply<qint32, qint32, double, double, double, Accuracy> reply = m_pos->GetPosition(); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(getPositionFinished(QDBusPendingCallWatcher*))); + + setOptions(); + + m_vel = new OrgFreedesktopGeoclueVelocityInterface(service, path, QDBusConnection::sessionBus()); + if (m_vel->isValid() && m_running) { + connect(m_vel, SIGNAL(VelocityChanged(qint32,qint32,double,double,double)), + this, SLOT(velocityChanged(qint32,qint32,double,double,double))); + } } void QGeoPositionInfoSourceGeoclueMaster::requestUpdateTimeout() @@ -457,70 +406,71 @@ void QGeoPositionInfoSourceGeoclueMaster::requestUpdateTimeout() // Only stop positioning if regular updates not active. if (!m_running) { cleanupPositionSource(); - releaseMasterClient(); + m_master->releaseMasterClient(); } } -void QGeoPositionInfoSourceGeoclueMaster::positionProviderChanged(const QByteArray &service, const QByteArray &path) +void QGeoPositionInfoSourceGeoclueMaster::getPositionFinished(QDBusPendingCallWatcher *watcher) { - if (m_pos) - cleanupPositionSource(); + QDBusPendingReply<qint32, qint32, double, double, double, Accuracy> reply = *watcher; + watcher->deleteLater(); - if (service.isEmpty() || path.isEmpty()) { - if (!m_regularUpdateTimedOut) { - m_regularUpdateTimedOut = true; - emit updateTimeout(); - } + if (reply.isError()) return; + + PositionFields fields = static_cast<PositionFields>(reply.argumentAt<0>()); + if (fields & Latitude && fields & Longitude) { + qint32 timestamp = reply.argumentAt<1>(); + double latitude = reply.argumentAt<2>(); + double longitude = reply.argumentAt<3>(); + double altitude = reply.argumentAt<4>(); + Accuracy accuracy = reply.argumentAt<5>(); + updatePosition(fields, timestamp, latitude, longitude, altitude, accuracy); } +} - m_pos = geoclue_position_new(service.constData(), path.constData()); - if (m_pos) { - if (m_running) { - g_signal_connect(G_OBJECT(m_pos), "position-changed", - G_CALLBACK(position_changed), this); - } +void QGeoPositionInfoSourceGeoclueMaster::positionChanged(qint32 fields, qint32 timestamp, double latitude, double longitude, double altitude, const Accuracy &accuracy) +{ + PositionFields pFields = static_cast<PositionFields>(fields); - // Get the current position immediately. - geoclue_position_get_position_async(m_pos, position_callback, this); - setOptions(); + if (pFields & Latitude && pFields & Longitude) + updatePosition(pFields, timestamp, latitude, longitude, altitude, accuracy); + else + positionUpdateFailed(); +} - m_vel = geoclue_velocity_new(service.constData(), path.constData()); - if (m_vel && m_running) { - g_signal_connect(G_OBJECT(m_vel), "velocity-changed", - G_CALLBACK(velocity_changed), this); - } - } +void QGeoPositionInfoSourceGeoclueMaster::velocityChanged(qint32 fields, qint32 timestamp, double speed, double direction, double climb) +{ + VelocityFields vFields = static_cast<VelocityFields>(fields); + + if (vFields == NoVelocityFields) + velocityUpdateFailed(); + else + updateVelocity(vFields, timestamp, speed, direction, climb); } void QGeoPositionInfoSourceGeoclueMaster::configurePositionSource() { - GeoclueAccuracyLevel accuracy; - GeoclueResourceFlags resourceFlags; + bool created = false; switch (preferredPositioningMethods()) { case SatellitePositioningMethods: - accuracy = GEOCLUE_ACCURACY_LEVEL_DETAILED; - resourceFlags = GEOCLUE_RESOURCE_GPS; + created = m_master->createMasterClient(Accuracy::Detailed, QGeoclueMaster::ResourceGps); break; case NonSatellitePositioningMethods: - accuracy = GEOCLUE_ACCURACY_LEVEL_NONE; - resourceFlags = GeoclueResourceFlags(GEOCLUE_RESOURCE_CELL | GEOCLUE_RESOURCE_NETWORK); + created = m_master->createMasterClient(Accuracy::None, QGeoclueMaster::ResourceCell | QGeoclueMaster::ResourceNetwork); break; case AllPositioningMethods: - accuracy = GEOCLUE_ACCURACY_LEVEL_NONE; - resourceFlags = GEOCLUE_RESOURCE_ALL; + created = m_master->createMasterClient(Accuracy::None, QGeoclueMaster::ResourceAll); break; default: - qWarning("GeoPositionInfoSourceGeoClueMaster unknown preferred method."); + qWarning("QGeoPositionInfoSourceGeoclueMaster unknown preferred method."); m_error = UnknownSourceError; emit QGeoPositionInfoSource::error(m_error); return; } - if (createMasterClient(accuracy, resourceFlags)) { - m_error = NoError; - } else { + if (!created) { m_error = UnknownSourceError; emit QGeoPositionInfoSource::error(m_error); } @@ -531,6 +481,4 @@ QGeoPositionInfoSource::Error QGeoPositionInfoSourceGeoclueMaster::error() const return m_error; } -#include "moc_qgeopositioninfosource_geocluemaster_p.cpp" - QT_END_NAMESPACE |