/**************************************************************************** ** ** Copyright (C) 2016 Jolla Ltd, author: Aaron McCarthy ** 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 #include #include #include 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(updateInterval()); } void QGeoSatelliteInfoSourceGeoclueMaster::stopUpdates() { if (!m_running) return; if (m_sat) { disconnect(m_sat, SIGNAL(SatelliteChanged(qint32,qint32,qint32,QList,QList)), this, SLOT(satelliteChanged(qint32,qint32,qint32,QList,QList))); } 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, QList > 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 &usedPrn, const QList &satInfos) { Q_UNUSED(timestamp) QList 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(updateInterval()); } 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, QList > 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 &usedPrn, const QList &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,QList)), this, SLOT(satelliteChanged(qint32,qint32,qint32,QList,QList))); } } 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(); QList usedPrn; dbusArgument >> usedPrn; dbusArgument = arguments.at(4).value(); QList 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