| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 Jolla Ltd, author: Aaron McCarthy <aaron.mccarthy@jollamobile.com> |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtPositioning module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qgeosatelliteinfosource_geocluemaster.h" |
| |
| #include <geoclue_interface.h> |
| #include <satellite_interface.h> |
| |
| #include <QtCore/QLoggingCategory> |
| #include <QtDBus/QDBusPendingCallWatcher> |
| |
| Q_DECLARE_LOGGING_CATEGORY(lcPositioningGeoclue) |
| |
| #define MINIMUM_UPDATE_INTERVAL 1000 |
| |
| QT_BEGIN_NAMESPACE |
| |
| QGeoSatelliteInfoSourceGeoclueMaster::QGeoSatelliteInfoSourceGeoclueMaster(QObject *parent) |
| : QGeoSatelliteInfoSource(parent), m_master(new QGeoclueMaster(this)), m_provider(0), m_sat(0), |
| m_requestTimer(this), m_error(NoError), m_satellitesChangedConnected(false), m_running(false) |
| { |
| connect(m_master, SIGNAL(positionProviderChanged(QString,QString,QString,QString)), |
| this, SLOT(positionProviderChanged(QString,QString,QString,QString))); |
| |
| m_requestTimer.setSingleShot(true); |
| connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestUpdateTimeout())); |
| } |
| |
| QGeoSatelliteInfoSourceGeoclueMaster::~QGeoSatelliteInfoSourceGeoclueMaster() |
| { |
| cleanupSatelliteSource(); |
| } |
| |
| int QGeoSatelliteInfoSourceGeoclueMaster::minimumUpdateInterval() const |
| { |
| return MINIMUM_UPDATE_INTERVAL; |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::setUpdateInterval(int msec) |
| { |
| if (msec < 0 || (msec > 0 && msec < MINIMUM_UPDATE_INTERVAL)) |
| msec = MINIMUM_UPDATE_INTERVAL; |
| |
| QGeoSatelliteInfoSource::setUpdateInterval(msec); |
| } |
| |
| QGeoSatelliteInfoSource::Error QGeoSatelliteInfoSourceGeoclueMaster::error() const |
| { |
| return m_error; |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::startUpdates() |
| { |
| if (m_running) |
| return; |
| |
| m_running = true; |
| |
| // Start Geoclue provider. |
| if (!m_master->hasMasterClient()) |
| configureSatelliteSource(); |
| |
| m_requestTimer.start(qMax(updateInterval(), minimumUpdateInterval())); |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::stopUpdates() |
| { |
| if (!m_running) |
| return; |
| |
| if (m_sat) { |
| disconnect(m_sat, SIGNAL(SatelliteChanged(qint32,qint32,qint32,QList<qint32>,QList<QGeoSatelliteInfo>)), |
| this, SLOT(satelliteChanged(qint32,qint32,qint32,QList<qint32>,QList<QGeoSatelliteInfo>))); |
| } |
| |
| m_running = false; |
| |
| // Only stop positioning if single update not requested. |
| if (!m_requestTimer.isActive()) { |
| cleanupSatelliteSource(); |
| m_master->releaseMasterClient(); |
| } |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::requestUpdate(int timeout) |
| { |
| if (timeout < minimumUpdateInterval() && timeout != 0) { |
| emit requestTimeout(); |
| return; |
| } |
| |
| if (m_requestTimer.isActive()) |
| return; |
| |
| if (!m_master->hasMasterClient()) |
| configureSatelliteSource(); |
| |
| m_requestTimer.start(qMax(timeout, minimumUpdateInterval())); |
| |
| if (m_sat) { |
| QDBusPendingReply<qint32, qint32, qint32, QList<qint32>, QList<QGeoSatelliteInfo> > reply = |
| m_sat->GetSatellite(); |
| QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); |
| connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), |
| this, SLOT(getSatelliteFinished(QDBusPendingCallWatcher*))); |
| } |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::updateSatelliteInfo(int timestamp, int satellitesUsed, |
| int satellitesVisible, |
| const QList<int> &usedPrn, |
| const QList<QGeoSatelliteInfo> &satInfos) |
| { |
| Q_UNUSED(timestamp); |
| |
| QList<QGeoSatelliteInfo> inUse; |
| |
| foreach (const QGeoSatelliteInfo &si, satInfos) |
| if (usedPrn.contains(si.satelliteIdentifier())) |
| inUse.append(si); |
| |
| if (satInfos.length() != satellitesVisible) { |
| qWarning("QGeoSatelliteInfoSourceGeoclueMaster number of in view QGeoSatelliteInfos (%d) " |
| "does not match expected number of in view satellites (%d).", satInfos.length(), |
| satellitesVisible); |
| } |
| |
| if (inUse.length() != satellitesUsed) { |
| qWarning("QGeoSatelliteInfoSourceGeoclueMaster number of in use QGeoSatelliteInfos (%d) " |
| "does not match expected number of in use satellites (%d).", inUse.length(), |
| satellitesUsed); |
| } |
| |
| m_inView = satInfos; |
| emit satellitesInViewUpdated(m_inView); |
| |
| m_inUse = inUse; |
| emit satellitesInUseUpdated(m_inUse); |
| |
| m_requestTimer.start(qMax(updateInterval(), minimumUpdateInterval())); |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::requestUpdateTimeout() |
| { |
| // If we end up here, there has not been a valid satellite info update. |
| if (m_running) { |
| m_inView.clear(); |
| m_inUse.clear(); |
| emit satellitesInViewUpdated(m_inView); |
| emit satellitesInUseUpdated(m_inUse); |
| } else { |
| emit requestTimeout(); |
| |
| // Only stop satellite info if regular updates not active. |
| cleanupSatelliteSource(); |
| m_master->releaseMasterClient(); |
| } |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::getSatelliteFinished(QDBusPendingCallWatcher *watcher) |
| { |
| QDBusPendingReply<qint32, qint32, qint32, QList<qint32>, QList<QGeoSatelliteInfo> > reply = *watcher; |
| watcher->deleteLater(); |
| |
| if (reply.isError()) |
| return; |
| |
| m_requestTimer.stop(); |
| updateSatelliteInfo(reply.argumentAt<0>(), reply.argumentAt<1>(), reply.argumentAt<2>(), |
| reply.argumentAt<3>(), reply.argumentAt<4>()); |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::satelliteChanged(int timestamp, int satellitesUsed, int satellitesVisible, const QList<int> &usedPrn, const QList<QGeoSatelliteInfo> &satInfos) |
| { |
| updateSatelliteInfo(timestamp, satellitesUsed, satellitesVisible, usedPrn, satInfos); |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::positionProviderChanged(const QString &name, |
| const QString &description, |
| const QString &service, |
| const QString &path) |
| { |
| Q_UNUSED(name); |
| Q_UNUSED(description); |
| |
| cleanupSatelliteSource(); |
| |
| QString providerService; |
| QString providerPath; |
| |
| if (service.isEmpty() || path.isEmpty()) { |
| // No valid position provider has been selected. This probably means that the GPS provider |
| // has not yet obtained a position fix. It can still provide satellite information though. |
| if (!m_satellitesChangedConnected) { |
| QDBusConnection conn = QDBusConnection::sessionBus(); |
| conn.connect(QString(), QString(), QStringLiteral("org.freedesktop.Geoclue.Satellite"), |
| QStringLiteral("SatelliteChanged"), this, |
| SLOT(satelliteChanged(QDBusMessage))); |
| m_satellitesChangedConnected = true; |
| return; |
| } |
| } else { |
| if (m_satellitesChangedConnected) { |
| QDBusConnection conn = QDBusConnection::sessionBus(); |
| conn.disconnect(QString(), QString(), |
| QStringLiteral("org.freedesktop.Geoclue.Satellite"), |
| QStringLiteral("SatelliteChanged"), this, |
| SLOT(satelliteChanged(QDBusMessage))); |
| m_satellitesChangedConnected = false; |
| } |
| |
| providerService = service; |
| providerPath = path; |
| } |
| |
| if (providerService.isEmpty() || providerPath.isEmpty()) { |
| m_error = AccessError; |
| emit QGeoSatelliteInfoSource::error(m_error); |
| return; |
| } |
| |
| m_provider = new OrgFreedesktopGeoclueInterface(providerService, providerPath, QDBusConnection::sessionBus()); |
| m_provider->AddReference(); |
| |
| m_sat = new OrgFreedesktopGeoclueSatelliteInterface(providerService, providerPath, QDBusConnection::sessionBus()); |
| |
| if (m_running) { |
| connect(m_sat, SIGNAL(SatelliteChanged(qint32,qint32,qint32,QList<qint32>,QList<QGeoSatelliteInfo>)), |
| this, SLOT(satelliteChanged(qint32,qint32,qint32,QList<qint32>,QList<QGeoSatelliteInfo>))); |
| } |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::satelliteChanged(const QDBusMessage &message) |
| { |
| QVariantList arguments = message.arguments(); |
| if (arguments.length() != 5) |
| return; |
| |
| int timestamp = arguments.at(0).toInt(); |
| int usedSatellites = arguments.at(1).toInt(); |
| int visibleSatellites = arguments.at(2).toInt(); |
| |
| QDBusArgument dbusArgument = arguments.at(3).value<QDBusArgument>(); |
| |
| QList<int> usedPrn; |
| dbusArgument >> usedPrn; |
| |
| dbusArgument = arguments.at(4).value<QDBusArgument>(); |
| |
| QList<QGeoSatelliteInfo> satelliteInfos; |
| dbusArgument >> satelliteInfos; |
| |
| satelliteChanged(timestamp, usedSatellites, visibleSatellites, usedPrn, satelliteInfos); |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::configureSatelliteSource() |
| { |
| if (!m_master->createMasterClient(Accuracy::Detailed, QGeoclueMaster::ResourceGps)) { |
| m_error = UnknownSourceError; |
| emit QGeoSatelliteInfoSource::error(m_error); |
| } |
| } |
| |
| void QGeoSatelliteInfoSourceGeoclueMaster::cleanupSatelliteSource() |
| { |
| if (m_provider) |
| m_provider->RemoveReference(); |
| delete m_provider; |
| m_provider = 0; |
| delete m_sat; |
| m_sat = 0; |
| } |
| |
| QT_END_NAMESPACE |