diff options
18 files changed, 4057 insertions, 0 deletions
diff --git a/src/plugins/position/blackberry/bb/ppsattribute.cpp b/src/plugins/position/blackberry/bb/ppsattribute.cpp new file mode 100644 index 00000000..e59f905a --- /dev/null +++ b/src/plugins/position/blackberry/bb/ppsattribute.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <bb/PpsAttribute> +#include "ppsattribute_p.h" + +#include <QDebug> +#include <QVariant> + +Q_DECLARE_METATYPE(QList<bb::PpsAttribute>) +typedef QMap<QString, bb::PpsAttribute> PpsAttributeMap; +Q_DECLARE_METATYPE(PpsAttributeMap) + +namespace bb +{ + +/////////////////////////// +// +// PpsAttributePrivate +// +/////////////////////////// + +PpsAttributePrivate::PpsAttributePrivate(): + _type(PpsAttribute::None) +{ +} + +/*static*/ PpsAttribute PpsAttributePrivate::ppsAttribute( int value, PpsAttributeFlag::Types flags ) +{ + PpsAttribute attribute; + attribute.d->_type = PpsAttribute::Number; + attribute.d->_data = value; + attribute.d->_flags = flags; + return attribute; +} + +/*static*/ PpsAttribute PpsAttributePrivate::ppsAttribute( long long value, PpsAttributeFlag::Types flags ) +{ + PpsAttribute attribute; + attribute.d->_type = PpsAttribute::Number; + attribute.d->_data = value; + attribute.d->_flags = flags; + return attribute; +} + +/*static*/ PpsAttribute PpsAttributePrivate::ppsAttribute( double value, PpsAttributeFlag::Types flags ) +{ + PpsAttribute attribute; + attribute.d->_type = PpsAttribute::Number; + attribute.d->_data = value; + attribute.d->_flags = flags; + return attribute; +} + +/*static*/ PpsAttribute PpsAttributePrivate::ppsAttribute( bool value, PpsAttributeFlag::Types flags ) +{ + PpsAttribute attribute; + attribute.d->_type = PpsAttribute::Bool; + attribute.d->_data = value; + attribute.d->_flags = flags; + return attribute; +} + +/*static*/ PpsAttribute PpsAttributePrivate::ppsAttribute( const QString &value, PpsAttributeFlag::Types flags ) +{ + PpsAttribute attribute; + attribute.d->_type = PpsAttribute::String; + attribute.d->_data = value; + attribute.d->_flags = flags; + return attribute; +} + +/*static*/ PpsAttribute PpsAttributePrivate::ppsAttribute( const QList<PpsAttribute> &value, PpsAttributeFlag::Types flags ) +{ + PpsAttribute attribute; + attribute.d->_type = PpsAttribute::Array; + attribute.d->_data = QVariant::fromValue(value); + attribute.d->_flags = flags; + return attribute; +} + +/*static*/ PpsAttribute PpsAttributePrivate::ppsAttribute( const QMap<QString, PpsAttribute> &value, PpsAttributeFlag::Types flags ) +{ + PpsAttribute attribute; + attribute.d->_type = PpsAttribute::Object; + attribute.d->_data = QVariant::fromValue(value); + attribute.d->_flags = flags; + return attribute; +} + +/////////////////////////// +// +// PpsAttribute +// +/////////////////////////// + +PpsAttribute::PpsAttribute(): + d(new PpsAttributePrivate()) +{ +} + +PpsAttribute::~PpsAttribute() +{ +} + +PpsAttribute::PpsAttribute(const PpsAttribute & other): + d(other.d) +{ +} + +PpsAttribute &PpsAttribute::operator=(const PpsAttribute & other) +{ + d = other.d; + return *this; +} + +bool PpsAttribute::operator==(const PpsAttribute & other) const +{ + if ( type() != other.type() ) { + return false; + } + if ( flags() != other.flags() ) { + return false; + } + + switch ( type() ) { + case PpsAttribute::Number: + case PpsAttribute::Bool: + case PpsAttribute::String: + // QVariant can compare double, int, longlong, bool, and QString for us. + return d->_data == other.d->_data; + case PpsAttribute::Array: + // QVariant can't compare custom types (like QList<PpsAttribute>), always returning false. So we pull + // the lists out manually and compare them. + return toList() == other.toList(); + case PpsAttribute::Object: + // QVariant can't compare custom types (like QMap<QString, PpsAttribute>), always returning false. So + // we pull the maps out manually and compare them. + return toMap() == other.toMap(); + case PpsAttribute::None: + // Both are "None" type, so the actual content doesn't matter. + return true; + } + return d->_data == other.d->_data; +} + +bool PpsAttribute::operator!=(const PpsAttribute & other) const +{ + return !(*this == other); +} + +bool PpsAttribute::isValid() const +{ + return d->_type != PpsAttribute::None; +} + +PpsAttribute::Type PpsAttribute::type() const +{ + return d->_type; +} + +bool PpsAttribute::isNumber() const +{ + return type() == PpsAttribute::Number; +} + +bool PpsAttribute::isBool() const +{ + return type() == PpsAttribute::Bool; +} + +bool PpsAttribute::isString() const +{ + return type() == PpsAttribute::String; +} + +bool PpsAttribute::isArray() const +{ + return type() == PpsAttribute::Array; +} + +bool PpsAttribute::isObject() const +{ + return type() == PpsAttribute::Object; +} + +double PpsAttribute::toDouble() const +{ + return d->_data.toDouble(); +} + +qlonglong PpsAttribute::toLongLong() const +{ + return d->_data.toLongLong(); +} + +int PpsAttribute::toInt() const +{ + return d->_data.toInt(); +} + +bool PpsAttribute::toBool() const +{ + return d->_data.toBool(); +} + +QString PpsAttribute::toString() const +{ + return d->_data.toString(); +} + +QList<PpsAttribute> PpsAttribute::toList() const +{ + return d->_data.value< QList<PpsAttribute> >(); +} + +QMap<QString, PpsAttribute> PpsAttribute::toMap() const +{ + return d->_data.value< QMap<QString, PpsAttribute> >(); +} + +PpsAttributeFlag::Types PpsAttribute::flags() const +{ + return d->_flags; +} + +QVariant PpsAttribute::toVariant() const +{ + return d->_data; +} + +QDebug operator<<(QDebug dbg, const PpsAttribute &attribute) +{ + dbg << "PpsAttribute("; + + switch ( attribute.type() ) { + case PpsAttribute::Number: + switch (attribute.toVariant().type()) { + case QVariant::Int: + dbg << "Number, " << attribute.flags() << ", " << attribute.toInt(); + break; + case QVariant::LongLong: + dbg << "Number, " << attribute.flags() << ", " << attribute.toLongLong(); + break; + default: + dbg << "Number, " << attribute.flags() << ", " << attribute.toDouble(); + break; + } + break; + case PpsAttribute::Bool: + dbg << "Bool, " << attribute.flags() << ", " << attribute.toBool(); + break; + case PpsAttribute::String: + dbg << "String, " << attribute.flags() << ", " << attribute.toString(); + break; + case PpsAttribute::Array: + dbg << "Array, " << attribute.flags() << ", " << attribute.toList(); + break; + case PpsAttribute::Object: + dbg << "Object, " << attribute.flags() << ", " << attribute.toMap(); + break; + case PpsAttribute::None: + dbg << "None"; + break; + } + + dbg << ')'; + + return dbg; +} + +} // namespace bb diff --git a/src/plugins/position/blackberry/bb/ppsattribute_p.h b/src/plugins/position/blackberry/bb/ppsattribute_p.h new file mode 100644 index 00000000..b3ffede1 --- /dev/null +++ b/src/plugins/position/blackberry/bb/ppsattribute_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BB_CORE_PPSATTRIBUTEPRIVATE_HPP +#define BB_CORE_PPSATTRIBUTEPRIVATE_HPP + +#include <bb/PpsAttribute> +#include <bb/PpsAttributeFlag> + +#include <QList> +#include <QMap> +#include <QSharedData> +#include <QString> +#include <QVariant> + +namespace bb +{ + +class PpsAttributePrivate : public QSharedData +{ +public: + PpsAttributePrivate(); + + static PpsAttribute ppsAttribute( double value, PpsAttributeFlag::Types flags ); + static PpsAttribute ppsAttribute( long long value, PpsAttributeFlag::Types flags ); + static PpsAttribute ppsAttribute( int value, PpsAttributeFlag::Types flags ); + static PpsAttribute ppsAttribute( bool value, PpsAttributeFlag::Types flags ); + static PpsAttribute ppsAttribute( const QString &value, PpsAttributeFlag::Types flags ); + static PpsAttribute ppsAttribute( const QList<PpsAttribute> &value, PpsAttributeFlag::Types flags ); + static PpsAttribute ppsAttribute( const QMap<QString, PpsAttribute> &value, PpsAttributeFlag::Types flags ); + +private: + friend class PpsAttribute; + + QVariant _data; + PpsAttribute::Type _type; + PpsAttributeFlag::Types _flags; +}; + +} // namespace bb + +#endif // BB_CORE_PPSOBJECTPRIVATE_HPP diff --git a/src/plugins/position/blackberry/bb/ppsobject.cpp b/src/plugins/position/blackberry/bb/ppsobject.cpp new file mode 100644 index 00000000..107a3a79 --- /dev/null +++ b/src/plugins/position/blackberry/bb/ppsobject.cpp @@ -0,0 +1,743 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Indicate which dependencies to mock when testing +// NOTE: These defines must occur before including any headers and +// ProxyInjector.hpp must be the first header included +#define USE_QT_QSOCKETNOTIFIER_PROXY +#define USE_UNISTD_PROXY +#define USE_FCNTL_PROXY +#define USE_ERRNO_PROXY +#define USE_STRING_PROXY +//#include <private/proxy/ProxyInjector.hpp> + +// Disable symbol renaming when testing for open, close, read, and +// write as these dependency names conflict with member method names +// and we don't want to rename the member methods. Instead, use dummy +// names for the dependencies in the production code and toggle +// between the real/proxy versions here. +#ifdef BB_TEST_BUILD +# undef open +# undef close +# undef read +# undef write +# define PosixOpen open_proxy +# define PosixClose close_proxy +# define PosixRead read_proxy +# define PosixWrite write_proxy +#else +# define PosixOpen ::open +# define PosixClose ::close +# define PosixRead ::read +# define PosixWrite ::write +#endif + +#include "ppsobject.h" + +#include "ppsattribute_p.h" +#include "safeassign.h" + +#include <bb/PpsAttribute> + +#include <QDebug> +#include <QObject> +#include <QSocketNotifier> + +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#include <exception> + +extern "C" { +#include "sys/pps.h" +} + +namespace bb +{ + +/** + * From "QNX Persistent Publish/Subscribe Developer's Guide": + * + * "the maximum size for a PPS object is 64 kilobytes" + */ +static const int PPS_MAX_SIZE = 64 * 1024; + +class PpsDecoder +{ +public: + PpsDecoder(char *str) + { + throwOnPpsDecoderError( pps_decoder_initialize(&_decoder, str) ); + } + + ~PpsDecoder() + { + pps_decoder_cleanup(&_decoder); + } + + QMap<QString, PpsAttribute> decode() + { + return decodeObject(); + } + + static QVariantMap variantMapFromPpsAttributeMap(const QMap<QString, PpsAttribute> &data) + { + QVariantMap variantMap; + + for ( QMap<QString, PpsAttribute>::const_iterator it = data.begin(); it != data.end(); it++ ) { + variantMap[it.key()] = variantFromPpsAttribute( it.value() ); + } + + return variantMap; + } + +private: + pps_decoder_t _decoder; + + static void throwOnPpsDecoderError(pps_decoder_error_t error) + { + switch ( error ) { + case PPS_DECODER_OK: + return; + default: + throw std::exception(); + } + } + + bb::PpsAttributeFlag::Types readFlags() + { + int rawFlags = pps_decoder_flags(&_decoder, NULL); + + bb::PpsAttributeFlag::Types attributeFlags; + + if ( rawFlags & PPS_INCOMPLETE ) { + attributeFlags |= bb::PpsAttributeFlag::Incomplete; + } + if ( rawFlags & PPS_DELETED ) { + attributeFlags |= bb::PpsAttributeFlag::Deleted; + } + if ( rawFlags & PPS_CREATED ) { + attributeFlags |= bb::PpsAttributeFlag::Created; + } + if ( rawFlags & PPS_TRUNCATED ) { + attributeFlags |= bb::PpsAttributeFlag::Truncated; + } + if ( rawFlags & PPS_PURGED ) { + attributeFlags |= bb::PpsAttributeFlag::Purged; + } + + return attributeFlags; + } + + bb::PpsAttribute decodeString() + { + const char * value = NULL; + throwOnPpsDecoderError( pps_decoder_get_string(&_decoder, NULL, &value) ); + bb::PpsAttributeFlag::Types flags = readFlags(); + return bb::PpsAttributePrivate::ppsAttribute( QString::fromUtf8(value), flags ); + } + + bb::PpsAttribute decodeNumber() + { + // In order to support more number types, we have to do something stupid because + // the PPS library won't let us work any other way + // Basically, we have to probe the encoded type in order to try to get exactly what + // we want + long long llValue; + double dValue; + int iValue; + bb::PpsAttributeFlag::Types flags; + + if (pps_decoder_is_integer( &_decoder, NULL )) { + int result = pps_decoder_get_int(&_decoder, NULL, &iValue ); + switch (result) { + case PPS_DECODER_OK: + flags = readFlags(); + return bb::PpsAttributePrivate::ppsAttribute( iValue, flags ); + case PPS_DECODER_CONVERSION_FAILED: + throwOnPpsDecoderError( pps_decoder_get_int64(&_decoder, NULL, &llValue) ); + flags = readFlags(); + return bb::PpsAttributePrivate::ppsAttribute( llValue, flags ); + default: + throw std::exception(); + } + } else { + throwOnPpsDecoderError( pps_decoder_get_double(&_decoder, NULL, &dValue)); + flags = readFlags(); + return bb::PpsAttributePrivate::ppsAttribute( dValue, flags ); + } + } + + bb::PpsAttribute decodeBool() + { + bool value; + throwOnPpsDecoderError( pps_decoder_get_bool(&_decoder, NULL, &value) ); + bb::PpsAttributeFlag::Types flags = readFlags(); + return bb::PpsAttributePrivate::ppsAttribute( value, flags ); + } + + bb::PpsAttribute decodeData() + { + pps_node_type_t nodeType = pps_decoder_type(&_decoder, NULL); + switch ( nodeType ) { + case PPS_TYPE_BOOL: + return decodeBool(); + case PPS_TYPE_NUMBER: + return decodeNumber(); + case PPS_TYPE_STRING: + return decodeString(); + case PPS_TYPE_ARRAY: { + // We must read the flags before we push into the array, otherwise we'll get the flags for the first element in the array. + bb::PpsAttributeFlag::Types flags = readFlags(); + throwOnPpsDecoderError( pps_decoder_push(&_decoder, NULL) ); + bb::PpsAttribute returnVal = bb::PpsAttributePrivate::ppsAttribute( decodeArray(), flags ); + throwOnPpsDecoderError( pps_decoder_pop(&_decoder) ); + return returnVal; + } + case PPS_TYPE_OBJECT: { + // We must read the flags before we push into the object, otherwise we'll get the flags for the first alement in the object. + bb::PpsAttributeFlag::Types flags = readFlags(); + throwOnPpsDecoderError( pps_decoder_push(&_decoder, NULL) ); + bb::PpsAttribute returnVal = bb::PpsAttributePrivate::ppsAttribute( decodeObject(), flags ); + throwOnPpsDecoderError( pps_decoder_pop(&_decoder) ); + return returnVal; + } + case PPS_TYPE_NULL: + case PPS_TYPE_NONE: + case PPS_TYPE_UNKNOWN: + case PPS_TYPE_DELETED: + default: + return bb::PpsAttribute(); + } + } + + QList<bb::PpsAttribute> decodeArray() + { + QList<bb::PpsAttribute> list; + + int length = pps_decoder_length( &_decoder ); + for ( int i = 0; i < length; i += 1 ) { + // Force movement to a specific index. + throwOnPpsDecoderError( pps_decoder_goto_index(&_decoder, i) ); + list << decodeData(); + } + + return list; + } + + QMap<QString, bb::PpsAttribute> decodeObject() + { + QMap<QString, bb::PpsAttribute> map; + + int length = pps_decoder_length(&_decoder); + for ( int i = 0; i < length; i += 1 ) { + // Force movement to a specific index. + throwOnPpsDecoderError( pps_decoder_goto_index(&_decoder, i) ); + QString name = QString::fromUtf8( pps_decoder_name(&_decoder) ); + map[name] = decodeData(); + } + + return map; + } + + static QVariant variantFromPpsAttribute(const PpsAttribute &attribute) + { + switch ( attribute.type() ) { + case PpsAttribute::Number: + switch (attribute.toVariant().type()) { + case QVariant::Int: + return attribute.toInt(); + break; + case QVariant::LongLong: + return attribute.toLongLong(); + break; + default: + return attribute.toDouble(); + break; + } + break; + case PpsAttribute::Bool: + return attribute.toBool(); + break; + case PpsAttribute::String: + return attribute.toString(); + break; + case PpsAttribute::Array: { + QVariantList variantList; + Q_FOREACH ( PpsAttribute attr, attribute.toList() ) { + variantList << variantFromPpsAttribute(attr); + } + return variantList; + } + case PpsAttribute::Object: { + return variantMapFromPpsAttributeMap( attribute.toMap() ); + } + case PpsAttribute::None: + default: + return QVariant(); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +class PpsEncoder +{ +public: + PpsEncoder() + { + pps_encoder_initialize(&_encoder, false); + } + + ~PpsEncoder() + { + pps_encoder_cleanup(&_encoder); + } + + QByteArray encode(const QVariantMap &ppsData) + { + encodeObject(ppsData); + const char *rawData = pps_encoder_buffer(&_encoder); + if (!rawData) { + throw std::exception(); + } + return QByteArray(rawData); + } + +private: + pps_encoder_t _encoder; + + static void throwOnPpsEncoderError(pps_encoder_error_t error) + { + switch ( error ) { + case PPS_ENCODER_OK: + return; + default: + throw std::exception(); + } + } + + void encodeData(const char *name, QVariant data) + { + switch ( data.type() ) { + case QVariant::Bool: + throwOnPpsEncoderError( pps_encoder_add_bool(&_encoder, name, data.toBool()) ); + break; + // We want to support encoding uint even though libpps doesn't support it directly. We can't encoding uint as an int + // since that will lose precision (e.g. 2^31+1 can't be encoded that way). However, we can convert uint to double + // without losing precision. QVariant.toDouble() conveniently takes care of the conversion for us. + case QVariant::UInt: + case QVariant::Double: + throwOnPpsEncoderError( pps_encoder_add_double(&_encoder, name, data.toDouble()) ); + break; + case QVariant::Int: + throwOnPpsEncoderError( pps_encoder_add_int(&_encoder, name, data.toInt()) ); + break; + case QVariant::LongLong: + throwOnPpsEncoderError( pps_encoder_add_int64(&_encoder, name, data.toLongLong()) ); + break; + case QVariant::String: + throwOnPpsEncoderError( pps_encoder_add_string(&_encoder, name, data.toString().toUtf8().constData()) ); + break; + case QVariant::List: { + throwOnPpsEncoderError( pps_encoder_start_array(&_encoder, name) ); + encodeArray( data.toList() ); + throwOnPpsEncoderError( pps_encoder_end_array(&_encoder) ); + break; + } + case QVariant::Map: { + throwOnPpsEncoderError( pps_encoder_start_object(&_encoder, name) ); + encodeObject( data.toMap() ); + throwOnPpsEncoderError( pps_encoder_end_object(&_encoder) ); + break; + } + case QVariant::Invalid: { + throwOnPpsEncoderError( pps_encoder_add_null(&_encoder, name) ); + break; + } + default: + throw std::exception(); + } + } + + void encodeArray(QVariantList data) + { + for ( QVariantList::const_iterator it = data.constBegin(); it != data.constEnd(); it ++ ) { + encodeData( NULL, *it ); + } + } + + void encodeObject(QVariantMap data) { + for ( QVariantMap::const_iterator it = data.constBegin(); it != data.constEnd(); it ++ ) { + encodeData( it.key().toUtf8().constData(), it.value() ); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +class PpsObjectPrivate +{ +public: + QSocketNotifier *_notifier; + QString _path; + mutable int _error; + int _fd; + bool _readyReadEnabled; + + explicit PpsObjectPrivate(const QString &path) : + _notifier(NULL), + _path(path), + _error(EOK), + _fd(-1), + _readyReadEnabled(true) + { + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +PpsObject::PpsObject(const QString &path, QObject *parent) : + QObject(parent), + d_ptr(new PpsObjectPrivate(path)) +{ +} + +PpsObject::~PpsObject() +{ + // RAII - ensure file gets closed + if (isOpen()) { + close(); + } +} + +int PpsObject::error() const +{ + Q_D(const PpsObject); + return d->_error; +} + +QString PpsObject::errorString() const +{ + Q_D(const PpsObject); + return QString(strerror(d->_error)); +} + +bool PpsObject::isReadyReadEnabled() const +{ + Q_D(const PpsObject); + + // query state of read ready signal + return d->_readyReadEnabled; +} + +void PpsObject::setReadyReadEnabled(bool enable) +{ + Q_D(PpsObject); + + // toggle whether socket notifier will emit a signal on read ready + d->_readyReadEnabled = enable; + if (isOpen()) { + d->_notifier->setEnabled(enable); + } +} + +bool PpsObject::isBlocking() const +{ + Q_D(const PpsObject); + + // reset last error + d->_error = EOK; + + // abort if file not open + if (!isOpen()) { + d->_error = EBADF; + return false; + } + + // query file status flags + int flags = fcntl(d->_fd, F_GETFL); + if (flags != -1) { + // check if non-blocking flag is unset + return ((flags & O_NONBLOCK) != O_NONBLOCK); + } else { + d->_error = errno; + return false; + } +} + +bool PpsObject::setBlocking(bool enable) +{ + Q_D(PpsObject); + + // reset last error + d->_error = EOK; + + // abort if file not open + if (!isOpen()) { + d->_error = EBADF; + return false; + } + + // query file status flags + int flags = fcntl(d->_fd, F_GETFL); + if (flags == -1) { + d->_error = errno; + return false; + } + + // configure non-blocking flag + if (enable) { + flags &= ~O_NONBLOCK; + } else { + flags |= O_NONBLOCK; + } + + // update file status flags + flags = fcntl(d->_fd, F_SETFL, flags); + if (flags == -1) { + d->_error = errno; + return false; + } + + return true; +} + +bool PpsObject::isOpen() const +{ + Q_D(const PpsObject); + return (d->_fd != -1); +} + +bool PpsObject::open(PpsOpenMode::Types mode) +{ + Q_D(PpsObject); + + // reset last error + d->_error = EOK; + + // abort if file already open + if (isOpen()) { + d->_error = EBUSY; + return false; + } + + // convert pps flags to open flags + int oflags = 0; + if ((mode & PpsOpenMode::Publish) && (mode & PpsOpenMode::Subscribe)) { + oflags |= O_RDWR; + } else if (mode & PpsOpenMode::Publish) { + oflags |= O_WRONLY; + } else if (mode & PpsOpenMode::Subscribe) { + oflags |= O_RDONLY; + } + + if (mode & PpsOpenMode::Create) { + oflags |= O_CREAT | O_EXCL; + } + + if (mode & PpsOpenMode::DeleteContents) { + oflags |= O_TRUNC; + } + + // open pps file + d->_fd = PosixOpen(d->_path.toUtf8().data(), oflags, 0666); + + // wire up socket notifier to know when reads are ready + if (d->_fd != -1) { + d->_notifier = new QSocketNotifier(d->_fd, QSocketNotifier::Read, this); + d->_notifier->setEnabled(d->_readyReadEnabled); + QObject::connect(d->_notifier, SIGNAL(activated(int)), this, SIGNAL(readyRead())); + return true; + } else { + d->_error = errno; + return false; + } +} + +bool PpsObject::close() +{ + Q_D(PpsObject); + + // reset last error + d->_error = EOK; + + // abort if file not open + if (!isOpen()) { + d->_error = EBADF; + return false; + } + + // shutdown socket notifier + delete d->_notifier; + d->_notifier = NULL; + + // close pps file + int result = PosixClose(d->_fd); + d->_fd = -1; + + // check success of operation + if (result == 0) { + return true; + } else { + d->_error = errno; + return false; + } +} + +QByteArray PpsObject::read(bool * ok) +{ + Q_D(PpsObject); + + // reset last error + d->_error = EOK; + + // abort if file not open + if (!isOpen()) { + d->_error = EBADF; + safeAssign(ok, false); + return QByteArray(); + } + + QByteArray byteArray; + byteArray.resize(PPS_MAX_SIZE); // resize doesn't initialize the data + int result = PosixRead(d->_fd, byteArray.data(), byteArray.size()); + + // check result of read operation + if (result > 0) { + // resize byte array to amount actually read + byteArray.resize(result); + safeAssign(ok, true); + return byteArray; + } else if (result == 0) { + // normalize the behavior of read() when no data is ready so a pps object + // put in non-blocking mode via opening w/o ?wait (read returns 0) looks + // the same as a pps object put in non-blocking mode by setting O_NONBLOCK + // (read returns EAGAIN) + d->_error = EAGAIN; + safeAssign(ok, false); + return QByteArray(); // Specifically return a default-constructed QByteArray. + } else { + d->_error = errno; + qWarning() << "PpsObject::read failed to read pps data, error " << errorString(); + safeAssign(ok, false); + return QByteArray(); // Specifically return a default-constructed QByteArray. + } +} + +bool PpsObject::write(const QByteArray &byteArray) +{ + Q_D(PpsObject); + + // reset last error + d->_error = EOK; + + // abort if file not open + if (!isOpen()) { + d->_error = EBADF; + return false; + } + + // write entire byte array to pps file + int result = PosixWrite(d->_fd, byteArray.data(), byteArray.size()); + if (result == -1) { + d->_error = errno; + } + return (result == byteArray.size()); +} + +bool PpsObject::remove() +{ + Q_D(PpsObject); + + // reset last error + d->_error = EOK; + + // delete pps file + int result = unlink(d->_path.toUtf8().data()); + + // check success of operation + if (result == 0) { + return true; + } else { + d->_error = errno; + return false; + } +} + +QVariantMap PpsObject::decode(const QByteArray &rawData, bool *ok) +{ + QMap<QString, PpsAttribute> mapData = decodeWithFlags(rawData, ok); + + // If *ok is false, then mapData is empty, so the resulting QVariantMap will also be empty, as desired. + return PpsDecoder::variantMapFromPpsAttributeMap(mapData); +} + +QMap<QString, PpsAttribute> PpsObject::decodeWithFlags(const QByteArray &rawData, bool *ok) +{ + bb::safeAssign(ok, true); + + try { + QByteArray mutableData(rawData); + PpsDecoder decoder(mutableData.data()); + return decoder.decode(); + } catch (...) { + bb::safeAssign(ok, false); + return QMap<QString, bb::PpsAttribute>(); + } +} + +QByteArray PpsObject::encode(const QVariantMap &ppsData, bool *ok) +{ + bb::safeAssign( ok, true ); + + try { + PpsEncoder encoder; + return encoder.encode(ppsData); + } catch (...) { + bb::safeAssign(ok, false); + return QByteArray(); + } +} + +} // namespace bb diff --git a/src/plugins/position/blackberry/bb/ppsobject.h b/src/plugins/position/blackberry/bb/ppsobject.h new file mode 100644 index 00000000..8468f40f --- /dev/null +++ b/src/plugins/position/blackberry/bb/ppsobject.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BB_CORE_PPSOBJECT_HPP +#define BB_CORE_PPSOBJECT_HPP + +#include <bb/Global> +#include <bb/PpsAttribute> +#include <bb/PpsOpenMode> + +#include <QMap> +#include <QObject> +#include <QVariantMap> + +namespace bb +{ +class PpsObjectPrivate; + +class BB_CORE_EXPORT PpsObject : public QObject +{ + Q_OBJECT + +public: + explicit PpsObject(const QString &path, QObject *parent = 0); + virtual ~PpsObject(); + + int error() const; + QString errorString() const; + + bool isReadyReadEnabled() const; + bool isBlocking() const; + bool setBlocking(bool enable); + bool isOpen() const; + bool open(PpsOpenMode::Types mode = PpsOpenMode::PublishSubscribe); + bool close(); + QByteArray read(bool * ok = 0); + bool write(const QByteArray &byteArray); + bool remove(); + + static QVariantMap decode( const QByteArray &rawData, bool * ok = 0 ); + static QMap<QString, PpsAttribute> decodeWithFlags( const QByteArray &rawData, bool * ok = 0 ); + static QByteArray encode( const QVariantMap &ppsData, bool * ok = 0 ); + +public Q_SLOTS: + void setReadyReadEnabled(bool enable); + +Q_SIGNALS: + void readyRead(); + +private: +//!@cond PRIVATE + QScopedPointer<PpsObjectPrivate> d_ptr; + Q_DECLARE_PRIVATE(PpsObject) + Q_DISABLE_COPY(PpsObject) +//!@endcond PRIVATE +}; + +} // namespace bb + +#endif // BB_CORE_PPSOBJECT_HPP diff --git a/src/plugins/position/blackberry/bb/safeassign.h b/src/plugins/position/blackberry/bb/safeassign.h new file mode 100644 index 00000000..a5128581 --- /dev/null +++ b/src/plugins/position/blackberry/bb/safeassign.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BB_CORE_SAFEASSIGN_HPP +#define BB_CORE_SAFEASSIGN_HPP + +namespace bb +{ + template <typename T> + void safeAssign( T * pointer, const T & value ) + { + if ( pointer ) { + *pointer = value; + } + } +} // namespace bb + +#endif // HEADER GUARD + diff --git a/src/plugins/position/blackberry/blackberry.pro b/src/plugins/position/blackberry/blackberry.pro new file mode 100644 index 00000000..e3543107 --- /dev/null +++ b/src/plugins/position/blackberry/blackberry.pro @@ -0,0 +1,27 @@ +TARGET = qtposition_blackberry +QT = core positioning + +PLUGIN_TYPE = position +load(qt_plugin) + +INCLUDEPATH += $$QT.location.includes + +LIBS = -lwmm -llocation_manager -lpps + +SOURCES += qgeopositioninfosource_bb.cpp \ + qgeosatelliteinfosource_bb.cpp \ + locationmanagerutil_bb.cpp \ + qgeopositioninfosourcefactory_bb.cpp +SOURCES += bb/ppsobject.cpp \ + bb/ppsattribute.cpp +HEADERS += qgeopositioninfosource_bb_p.h \ + qgeopositioninfosource_bb.h \ + qgeosatelliteinfosource_bb_p.h \ + qgeosatelliteinfosource_bb.h \ + locationmanagerutil_bb.h \ + qgeopositioninfosourcefactory_bb.h +HEADERS += bb/ppsobject.h \ + bb/ppsattribute_p.h + +OTHER_FILES += \ + plugin.json diff --git a/src/plugins/position/blackberry/locationmanagerutil_bb.cpp b/src/plugins/position/blackberry/locationmanagerutil_bb.cpp new file mode 100644 index 00000000..f9541724 --- /dev/null +++ b/src/plugins/position/blackberry/locationmanagerutil_bb.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "locationmanagerutil_bb.h" + +#include <bb/PpsObject> + +#include <QtCore/QVariantMap> +#include <QtCore/QByteArray> +#include <QtDebug> + +#include <errno.h> + +namespace { + +// Create a QVariantMap suitable for writing to a PpsObject specifying a cancel request to the +// Location Manager. +QVariantMap createCancelRequest() +{ + QVariantMap map; + + map.insert("msg", "cancel"); + map.insert("id", ::global::libQtLocationId); + + return map; +} + +} // unnamed namespace + +namespace global { + +const QString libQtLocationId = "libQtLocation"; +const QString locationManagerPpsFile = "/pps/services/geolocation/control"; +const int minUpdateInterval = 1000; +const QVariantMap cancelRequest = createCancelRequest(); + +} // namespace global + + +// send a generic server-mode request, wrapped in a @control map, to ppsObject +bool sendRequest(bb::PpsObject &ppsObject, const QVariantMap &request) +{ + if (!ppsObject.isOpen()) { + if (!ppsObject.open()) { + qWarning() << "LocationManagerUtil.cpp:sendRequest(): error opening pps object, errno =" + << ppsObject.error() << "(" + << strerror(ppsObject.error()) + << "). Clients should verify that they have access_location_services " + "permission."; + return false; + } + } + + // wrap the request in a @control map + QVariantMap map; + map.insert("@control", request); + + + // encode it + bool ok; + QByteArray encodedRequest = bb::PpsObject::encode(map, &ok); + if (!ok) { + qWarning() << "LocationManagerUtil.cpp:sendRequest(): error encoding position request"; + ppsObject.close(); + return false; + } + + // write it + bool success = ppsObject.write(encodedRequest); + if (!success) { + qWarning() << "LocationManagerUtil.cpp:sendRequest(): error" + << ppsObject.error() + << "writing position request"; + ppsObject.close(); + return false; + } + + return true; +} + +// receive a generic server-mode reply from ppsObject, removing the @control map container +bool receiveReply(QVariantMap *reply, bb::PpsObject &ppsObject) +{ + if (!ppsObject.isOpen()) { + if (!ppsObject.open()) { + qWarning() << "LocationManagerUtil.cpp:receiveReply(): error opening pps object"; + return false; + } + } + + // read the reply + bool ok; + QByteArray encodedReply = ppsObject.read(&ok); + if (!ok) { + qWarning() << "LocationManagerUtil.cpp:receiveReply(): error" + << ppsObject.error() + << "reading position reply"; + ppsObject.close(); + return false; + } + + // decode the reply + *reply = bb::PpsObject::decode(encodedReply, &ok); + if (!ok) { + qWarning() << "LocationManagerUtil.cpp:receiveReply(): error decoding position reply"; + ppsObject.close(); + return false; + } + + // peel out the control map from the reply + *reply = reply->value("@control").toMap(); + + // check for an error in the reply + if (reply->contains("errCode")) { + int errCode = reply->value("errCode").toInt(); + if (errCode) { + qWarning() << "LocationManagerUtil.cpp:receiveReply(): (" << errCode << ")" << + reply->value("err").toString().toLocal8Bit().constData() << ":" << + reply->value("errstr").toString().toLocal8Bit().constData(); + return false; + } + } + + return true; +} + diff --git a/src/plugins/position/blackberry/locationmanagerutil_bb.h b/src/plugins/position/blackberry/locationmanagerutil_bb.h new file mode 100644 index 00000000..59d9cd52 --- /dev/null +++ b/src/plugins/position/blackberry/locationmanagerutil_bb.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOCATIONMANAGERUTIL_BB_H +#define LOCATIONMANAGERUTIL_BB_H + +#include <QtCore/QVariantMap> + +namespace bb +{ +class PpsObject; +} + +namespace global { + +// the libQtLocation id for the server-mode accessed pps file id field +extern const QString libQtLocationId; + +// the path to the location manager pps file that is the gateway for positioning requests/replies +extern const QString locationManagerPpsFile; + +// the minimum interval (in msec) that positional and satellite updates can be provided for +extern const int minUpdateInterval; + +// a QVariantMap suitable for writing to a PpsObject specifying a cancel request to the Location +// Manager. This cancels the current request +extern const QVariantMap cancelRequest; + +} // namespace global + +// send a generic server-mode request, wrapped in a @control map, to ppsObject +bool sendRequest(bb::PpsObject &ppsObject, const QVariantMap &request); + +// receive a generic server-mode reply from ppsObject, removing the @control map container +bool receiveReply(QVariantMap *reply, bb::PpsObject &ppsObject); + +#endif diff --git a/src/plugins/position/blackberry/plugin.json b/src/plugins/position/blackberry/plugin.json new file mode 100644 index 00000000..dca8e6a4 --- /dev/null +++ b/src/plugins/position/blackberry/plugin.json @@ -0,0 +1,8 @@ +{ + "Keys": ["blackberry"], + "Provider": "blackberry", + "Position": true, + "Satellite": true, + "Monitor": false, + "Priority": 1000 +} diff --git a/src/plugins/position/blackberry/qgeopositioninfosource_bb.cpp b/src/plugins/position/blackberry/qgeopositioninfosource_bb.cpp new file mode 100644 index 00000000..80e215e0 --- /dev/null +++ b/src/plugins/position/blackberry/qgeopositioninfosource_bb.cpp @@ -0,0 +1,1276 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosource_bb_p.h" +#include "locationmanagerutil_bb.h" + +#ifndef BB_TEST_BUILD +# include <bb/PpsObject> +#else +# include "../tests/include/PpsObjectStuntDouble.hpp" +#endif + +extern "C" { +#include <wmm/wmm.h> +} + +#include <location_manager.h> + +#include <QMap> +#include <QVariantMap> +#include <QByteArray> +#include <QtDebug> +#include <QStringList> + +#include <errno.h> + +/////////////////////////// +// +// local variables/functions +// +/////////////////////////// + +namespace global +{ + +// Currently the default behavior for Location Manager is simply to set a constant default +// interval. 5 sec has been chosen as a compromise between timely updates and conserving power. +static const int defaultPositionUpdatePeriod = 5; + +} // namespace global + +namespace +{ + +// map the Location Manager reply error codes to the PositionErrorCode enum values. +QMap<int, bb::location::PositionErrorCode::Type> createIntToPositionErrorCodeMap() +{ + QMap<int, bb::location::PositionErrorCode::Type> map; + + map.insert(0, bb::location::PositionErrorCode::None); + map.insert(1, bb::location::PositionErrorCode::FatalDisabled); + map.insert(2, bb::location::PositionErrorCode::FatalNoLastKnownPosition); + map.insert(3, bb::location::PositionErrorCode::FatalInsufficientProviders); + map.insert(4, bb::location::PositionErrorCode::FatalInvalidRequest); + map.insert(5, bb::location::PositionErrorCode::FatalUnknown); + map.insert(6, bb::location::PositionErrorCode::FatalPermission); + map.insert(0x10000, bb::location::PositionErrorCode::WarnTimeout); + map.insert(0x10001, bb::location::PositionErrorCode::WarnLostTracking); + map.insert(0x10002, bb::location::PositionErrorCode::WarnStationary); + + return map; +} + +const QMap<int, bb::location::PositionErrorCode::Type> intToPositionErrorCodeMap = + createIntToPositionErrorCodeMap(); + +bool fatalError(bb::location::PositionErrorCode::Type code) +{ + if ( code == bb::location::PositionErrorCode::FatalDisabled + || code == bb::location::PositionErrorCode::FatalNoLastKnownPosition + || code == bb::location::PositionErrorCode::FatalInsufficientProviders + || code == bb::location::PositionErrorCode::FatalInvalidRequest + || code == bb::location::PositionErrorCode::FatalUnknown + || code == bb::location::PositionErrorCode::FatalPermission ) { + return true; + } + return false; +} + +// map the PositionErrorCode enum values to the Qt5 QGeoPositionInfoSource::Error enum values. +QMap<bb::location::PositionErrorCode::Type, QGeoPositionInfoSource::Error> + createPositionErrorCodeToErrorMap() +{ + QMap<bb::location::PositionErrorCode::Type, QGeoPositionInfoSource::Error> map; + + map.insert(bb::location::PositionErrorCode::FatalDisabled, + QGeoPositionInfoSource::ClosedError); + map.insert(bb::location::PositionErrorCode::FatalNoLastKnownPosition, + QGeoPositionInfoSource::UnknownSourceError); + map.insert(bb::location::PositionErrorCode::FatalInsufficientProviders, + QGeoPositionInfoSource::UnknownSourceError); + map.insert(bb::location::PositionErrorCode::FatalInvalidRequest, + QGeoPositionInfoSource::UnknownSourceError); + map.insert(bb::location::PositionErrorCode::FatalUnknown, + QGeoPositionInfoSource::UnknownSourceError); + map.insert(bb::location::PositionErrorCode::FatalPermission, + QGeoPositionInfoSource::AccessError); + + return map; +} + +const QMap<bb::location::PositionErrorCode::Type, QGeoPositionInfoSource::Error> + positionErrorCodeToErrorMap = createPositionErrorCodeToErrorMap(); + +// map the Location Manager provider names to the QGeoPositionInfoSource positioning methods +QMap<QGeoPositionInfoSource::PositioningMethods, QString> createPositioningMethodsToProviderMap() +{ + QMap<QGeoPositionInfoSource::PositioningMethods, QString> map; + + map.insert(QGeoPositionInfoSource::SatellitePositioningMethods, QString("gnss")); + map.insert(QGeoPositionInfoSource::NonSatellitePositioningMethods, QString("network")); + map.insert(QGeoPositionInfoSource::AllPositioningMethods, QString("hybrid")); + + return map; +} + +const QMap<QGeoPositionInfoSource::PositioningMethods, QString> + positioningMethodsToProviderMap = createPositioningMethodsToProviderMap(); + +// list of valid strings for the Location Manager reset types +QStringList createValidResetTypesList() +{ + QStringList list; + list.append("cold"); + list.append("warm"); + list.append("hot"); + list.append("factory"); + list.append("ee_data"); + list.append("almanac"); + list.append("ephemeris"); + + return list; +} + +const QStringList validResetTypes = createValidResetTypesList(); + +void printGetGeomagneticFieldInputs(const wmm_location_t &location, const struct tm &date) +{ + qWarning() << "location = (" + << location.latitude_deg + << "," + << location.longitude_deg + << "," + << location.altitude_meters + << ")"; + qWarning() << "date = (" << date.tm_sec << + "," << date.tm_min << + "," << date.tm_hour << + "," << date.tm_mday << + "," << date.tm_mon << + "," << date.tm_year << + "," << date.tm_wday << + "," << date.tm_yday << + "," << date.tm_isdst << +#ifndef BB_TEST_BUILD +// the following fields are not present on host (at least Win32) + "," << date.tm_gmtoff << + "," << date.tm_zone << +#endif + ")"; +} + +bool magneticDeclination(double *declination, const QGeoPositionInfo &position) +{ + if (!declination) + return false; + + wmm_location_t location; + struct tm date; + wmm_geomagnetic_field_t field; + + location.latitude_deg = position.coordinate().latitude(); + location.longitude_deg = position.coordinate().longitude(); + if (position.coordinate().type() == QGeoCoordinate::Coordinate3D) + location.altitude_meters = position.coordinate().altitude(); + else + location.altitude_meters = 0.0; + + time_t time = (time_t)position.timestamp().toTime_t(); +#ifdef BB_TEST_BUILD + // since gmtime_r() is not defined on host (at least Win32) risk reentrant side effects on the + // returned data. + struct tm *pDate = gmtime(&time); + if (pDate == NULL) { + qWarning() << "QGeoPositionInfoSourceBbPrivate.cpp:magneticDeclination(): " + "gmtime() returned NULL"; + return false; + } + date = *pDate; +#else + if (gmtime_r(&time, &date) == NULL) { + qWarning() << "QGeoPositionInfoSourceBbPrivate.cpp:magneticDeclination(): " + "gmtime_r() returned NULL"; + return false; + } +#endif + + switch (wmm_get_geomagnetic_field(&location, &date, &field)) { + case 0: + break; + + case 1: + qWarning() << "QGeoPositionInfoSourceBbPrivate.cpp:magneticDeclination(): " + "wmm_get_geomagnetic_field() returned: inputs limited to model range"; + printGetGeomagneticFieldInputs(location, date); + break; + + case -1: + default: + qWarning() << "QGeoPositionInfoSourceBbPrivate.cpp:magneticDeclination(): " + "wmm_get_geomagnetic_field() returned: error"; + printGetGeomagneticFieldInputs(location, date); + return false; + } + + *declination = field.declination_deg; + return true; +} + +QVariantMap populateLastKnownPositionRequest(bool fromSatellitePositioningMethodsOnly) +{ + QVariantMap map; + QVariantMap datMap; + + if (fromSatellitePositioningMethodsOnly) + datMap.insert("provider", "gnss"); + else + datMap.insert("provider", "hybrid"); + + datMap.insert("last_known", true); + datMap.insert("period", 0); + + map.insert("msg", "location"); + map.insert("id", ::global::libQtLocationId); + map.insert("dat", datMap); + + return map; +} + +// From a QvariantMap representing a location response from the Location Manager fill a +// QGeoPositionInfo instance intended to be emitted via positionUpdated() signal. Returns true +// if the position info was successfully populated. +bool populatePositionInfo(QGeoPositionInfo *position, const QVariantMap &map) +{ + // populate position + + // set the reply dat property, which can be accessed by the user in the slot connected to + //the positionUpdated() signal + QVariantMap replyDat = map.value("dat").toMap(); + + // check for required fields + if (!replyDat.contains("latitude") || !replyDat.contains("longitude") + || !replyDat.contains("accuracy")) { + return false; + } + + // set the lat/long/alt coordinate + QGeoCoordinate coord; + coord.setLatitude(replyDat.value("latitude").toDouble()); + coord.setLongitude(replyDat.value("longitude").toDouble()); + if (replyDat.contains("altitude")) + coord.setAltitude(replyDat.value("altitude").toDouble()); + + if (!coord.isValid()) + return false; + + position->setCoordinate(coord); + + // set the time stamp + QDateTime dateTime; + dateTime.setTimeSpec(Qt::UTC); + if (replyDat.contains("utc") && static_cast<int>(replyDat.value("utc").toDouble()) != 0) { + // utc is msec since epoch (1970-01-01T00:00:00) + dateTime.setTime_t(qRound((replyDat.value("utc").toDouble() / 1000.0))); + } else { + // this relies on the device's clock being accurate + dateTime = QDateTime::currentDateTimeUtc(); + } + position->setTimestamp(dateTime); + + // attributes + if (replyDat.contains("heading")) { + position->setAttribute(QGeoPositionInfo::Direction, + static_cast<qreal>(replyDat.value("heading").toDouble())); + } else { + position->removeAttribute(QGeoPositionInfo::Direction); + } + + if (replyDat.contains("speed")) { + position->setAttribute(QGeoPositionInfo::GroundSpeed, + static_cast<qreal>(replyDat.value("speed").toDouble())); + } else { + position->removeAttribute(QGeoPositionInfo::GroundSpeed); + } + + if (replyDat.contains("verticalSpeed")) { + position->setAttribute(QGeoPositionInfo::VerticalSpeed, + static_cast<qreal>(replyDat.value("verticalSpeed").toDouble())); + } else { + position->removeAttribute(QGeoPositionInfo::VerticalSpeed); + } + + if (replyDat.contains("declination")) { + position->setAttribute(QGeoPositionInfo::MagneticVariation, + static_cast<qreal>(replyDat.value("declination").toDouble())); + } else { + double declination; + + if (magneticDeclination(&declination, *position) == true) { + position->setAttribute(QGeoPositionInfo::MagneticVariation, + static_cast<qreal>(declination)); + } else { + position->removeAttribute(QGeoPositionInfo::MagneticVariation); + } + } + + // replyDat.contains("accuracy") was confirmed above + position->setAttribute(QGeoPositionInfo::HorizontalAccuracy, + static_cast<qreal>(replyDat.value("accuracy").toDouble())); + + if (replyDat.contains("altitudeAccuracy")) { + position->setAttribute(QGeoPositionInfo::VerticalAccuracy, + static_cast<qreal>(replyDat.value("altitudeAccuracy").toDouble())); + } else { + position->removeAttribute(QGeoPositionInfo::VerticalAccuracy); + } + + return true; +} + +} // unnamed namespace + +/////////////////////////// +// +// QGeoPositionInfoSourceBbPrivate +// +/////////////////////////// + +// Create a QVariantMap suitable for writing to a PpsObject specifying a location request to the +// Location Manager. If the request is periodic then the update interval is used. Otherwise 0 +// indicates to the Location Manager that it is a request for a single, immediate location response. +// singleRequestMsec applies only to the single, immediate location response. It represents the +// expected location response time, after which it is assumed a timeout response occurs. +QVariantMap QGeoPositionInfoSourceBbPrivate::populateLocationRequest(bool periodic, + int singleRequestMsec) const +{ + Q_Q(const QGeoPositionInfoSourceBb); + + QVariantMap map; + QVariantMap datMap; + + int period; + int responseTime; + if (periodic) { + // rounding is performed here because the Location Manager truncates to nearest integer + period = (q->updateInterval() + 500) / 1000; + // The Qt MObility API treats a period of 0 as indicating default behavior + if (period == 0) { + // specify global::defaultPositionUpdatePeriod as the default behavior for Location + // Manager + period = ::global::defaultPositionUpdatePeriod; + } + responseTime = qRound(_responseTime); + } else { + period = 0; + responseTime = (singleRequestMsec + 500) / 1000; + } + + // period is the only mandatory field + datMap.insert("period", period); + + if (_accuracy > 0.0) + datMap.insert("accuracy", _accuracy); + if (responseTime > 0.0) + datMap.insert("response_time", responseTime); + + // since there is no uninitialized state for bool always specify the background mode + datMap.insert("background", _canRunInBackground); + + QString provider = positioningMethodsToProviderMap.value(q->preferredPositioningMethods()); + if (!provider.isEmpty()) + datMap.insert("provider", provider); + + if (!_fixType.isEmpty()) + datMap.insert("fix_type", _fixType); + + if (!_appId.isEmpty()) + datMap.insert("app_id", _appId); + + if (!_appPassword.isEmpty()) + datMap.insert("app_password", _appPassword); + + if (!_pdeUrl.isEmpty()) + datMap.insert("pde_url", _pdeUrl.toEncoded().constData()); + + if (!_slpUrl.isEmpty()) + datMap.insert("slp_url", _slpUrl.toEncoded().constData()); + + map.insert("msg", "location"); + map.insert("id", global::libQtLocationId); + map.insert("dat", datMap); + + return map; +} + +QVariantMap QGeoPositionInfoSourceBbPrivate::populateResetRequest() const +{ + QVariantMap map; + QVariantMap datMap; + + datMap.insert("reset_type", _resetType); + + map.insert("msg", "reset"); + map.insert("id", ::global::libQtLocationId); + map.insert("dat", datMap); + + return map; +} + +bool QGeoPositionInfoSourceBbPrivate::requestPositionInfo(bool periodic, int singleRequestMsec) +{ + // build up the request + QVariantMap request = populateLocationRequest(periodic, singleRequestMsec); + + bb::PpsObject *ppsObject; + if (periodic) + ppsObject = _periodicUpdatePpsObject; + else + ppsObject = _singleUpdatePpsObject; + + bool returnVal = sendRequest(*ppsObject, request); +#ifndef BB_TEST_BUILD + if (!returnVal) { + // test for pps file error + switch (ppsObject->error()) { + case EACCES: + _replyErrorCode = bb::location::PositionErrorCode::FatalPermission; + _replyErr = "failed"; + _replyErrStr = ppsObject->errorString(); + break; + + case EOK: + _replyErrorCode = bb::location::PositionErrorCode::FatalUnknown; + _replyErr = "failed"; + _replyErrStr = "Unknown error occurred sending request"; + qWarning() << "QGeoPositionInfoSourceBbPrivate::requestPositionInfo() :" + << _replyErrStr; + break; + + default: + _replyErrorCode = bb::location::PositionErrorCode::FatalUnknown; + _replyErr = "failed"; + _replyErrStr = ppsObject->errorString(); + qWarning() << "QGeoPositionInfoSourceBbPrivate::requestPositionInfo() : " + "unexpected error, errno =" + << ppsObject->error() + << " (" + << ppsObject->errorString() + << ")"; + break; + } + } +#endif // !BB_TEST_BUILD + + return returnVal; +} + +void QGeoPositionInfoSourceBbPrivate::cancelPositionInfo(bool periodic) +{ + bb::PpsObject *ppsObject; + if (periodic) + ppsObject = _periodicUpdatePpsObject; + else + ppsObject = _singleUpdatePpsObject; + + (void)sendRequest(*ppsObject, global::cancelRequest); +} + +void QGeoPositionInfoSourceBbPrivate::resetLocationProviders() +{ + QVariantMap map = populateResetRequest(); + (void)sendRequest(*_periodicUpdatePpsObject, map); +} + +// Get the last known position from the Location Manager. Any error results in the return of an +// invalid position. +QGeoPositionInfo QGeoPositionInfoSourceBbPrivate::lastKnownPosition( + bool fromSatellitePositioningMethodsOnly) const +{ + QGeoPositionInfo position = QGeoPositionInfo(); + bb::PpsObject ppsObject(global::locationManagerPpsFile); + QVariantMap lastKnown = populateLastKnownPositionRequest(fromSatellitePositioningMethodsOnly); + + if (!ppsObject.open()) + return position; + + // Location Manager promises to reply immediately with the last known position or an error. + ppsObject.setBlocking(true); + + if (!sendRequest(ppsObject, lastKnown)) + return position; + + if (!receiveReply(&lastKnown, ppsObject)) + return position; + + if (!lastKnown.contains("res") || lastKnown.value("res").toString() != "location") + return position; + + // the return value of populatePositionInfo() is ignored since either way position is returned + // by lastKnownPosition() + (void)populatePositionInfo(&position, lastKnown); + + return position; +} + +// Constructor. Note there are two PpsObjects for handling the two different types of requests that +// can be simultaneously made and which must be handled independently (apart from both being +// emitted through the same signal when done-part of Qt Mobility spec. +QGeoPositionInfoSourceBbPrivate::QGeoPositionInfoSourceBbPrivate(QGeoPositionInfoSourceBb *parent) + : QObject(parent), + _startUpdatesInvoked(false), + _requestUpdateInvoked(false), + _canEmitPeriodicUpdatesTimeout(true), + q_ptr(parent), + _periodicUpdatePpsObject(new bb::PpsObject(global::locationManagerPpsFile, this)), + _singleUpdatePpsObject(new bb::PpsObject(global::locationManagerPpsFile, this)), + _sourceError(QGeoPositionInfoSource::NoError), + _accuracy(0.0), + _responseTime(0.0), + _canRunInBackground(false), + _fixType(QString()), + _appId(QString()), + _appPassword(QString()), + _pdeUrl(QUrl()), + _slpUrl(QUrl()), + _replyDat(QVariantMap()), + _replyErrorCode(bb::location::PositionErrorCode::None), + _replyErr(QString()), + _replyErrStr(QString()), + _resetType(QString()) +{ + // register bb::location::PositionErrorCode::Type so it can be used with QObject::property() + qRegisterMetaType<bb::location::PositionErrorCode::Type>(); + + // connect to periodic update PpsObject::readyRead() + connect(_periodicUpdatePpsObject, SIGNAL(readyRead()), SLOT(receivePeriodicPositionReply())); + + // connect to single update PpsObject::readyRead() + connect(_singleUpdatePpsObject, SIGNAL(readyRead()), SLOT(receiveSinglePositionReply())); + + // queued connection to signal updateTimeout() + connect(this, SIGNAL(queuedUpdateTimeout()), SLOT(emitUpdateTimeout()), Qt::QueuedConnection); +} + +QGeoPositionInfoSourceBbPrivate::~QGeoPositionInfoSourceBbPrivate() +{ + stopUpdates(); +} + +// request periodic updates +void QGeoPositionInfoSourceBbPrivate::startUpdates() +{ + // do nothing if periodic updates have already been started + if (_startUpdatesInvoked) + return; + + // This flag is used to limit emitting the timeout signal to once per each interruption in the + // periodic updates. Since updates are being started here ensure the flag is set to true. + _canEmitPeriodicUpdatesTimeout = true; + + // build a request and initiate it + if (requestPositionInfo(true)) { + _startUpdatesInvoked = true; + _currentPosition = QGeoPositionInfo(); + } else { + // With Qt5 the error() signal was introduced. If there are any receivers emit error() else + // maintain QtMobility behavior. + _sourceError + = positionErrorCodeToErrorMap.value(_replyErrorCode, + QGeoPositionInfoSource::UnknownSourceError); + Q_Q(QGeoPositionInfoSourceBb); + if (q->receivers(SIGNAL(error(QGeoPositionInfoSource::Error)))) { + Q_EMIT ((QGeoPositionInfoSource *)q)->error(_sourceError); + } else { + // user is expecting a signal to be emitted, cannot emit positionUpdated() because of + // error so emit timeout signal. The connection is queued because it is possible for the + // user to call startUpdates() from within the slot handling the timeout. The queued + // connection avoids potential infinite recursion. + Q_EMIT queuedUpdateTimeout(); + } + } +} + +// stop periodic updates +void QGeoPositionInfoSourceBbPrivate::stopUpdates() +{ + // do nothing if periodic updates have not been started + if (!_startUpdatesInvoked) + return; + + cancelPositionInfo(true); + _startUpdatesInvoked = false; + _currentPosition = QGeoPositionInfo(); + + // close the pps file to ensure readyRead() does not spin in the event that we don't read the + // reply to the cancel request. Note that open() is done lazily in sendRequest(). + _periodicUpdatePpsObject->close(); +} + +// periodic updates have timed out +void QGeoPositionInfoSourceBbPrivate::periodicUpdatesTimeout() +{ + // do nothing if periodic updates have not been started + if (!_startUpdatesInvoked) + return; + + // timeout has occurred, but periodic updates are still active. Ensure the timeout signal is + // emitted only once per interruption of updates. _canEmitPeriodicUpdatesTimeout is set back + // to true when the next successful periodic update occurs (see emitPositionUpdated()). This + // behavior is per the Qt Mobility Location API documentation. + if (_canEmitPeriodicUpdatesTimeout) { + _canEmitPeriodicUpdatesTimeout = false; + emitUpdateTimeout(); + } +} + +// request single update +void QGeoPositionInfoSourceBbPrivate::requestUpdate(int msec) +{ + // do nothing if an immediate update has already been requested + if (_requestUpdateInvoked) + return; + + if (msec) { + // If it is not possible to update in msec timeout immediately. + Q_Q(QGeoPositionInfoSourceBb); + if (msec < q->minimumUpdateInterval()) { + // The connection is queued because it is possible for the user to call requestUpdate() + // from within the slot handling the timeout. The queued connection avoids potential + // infinite recursion. + Q_EMIT queuedUpdateTimeout(); + return; + } + } + + if (requestPositionInfo(false, msec)) { + _requestUpdateInvoked = true; + } else { + // With Qt5 the error() signal was introduced. If there are any receivers emit error() else + // maintain QtMobility behavior. + _sourceError + = positionErrorCodeToErrorMap.value(_replyErrorCode, + QGeoPositionInfoSource::UnknownSourceError); + Q_Q(QGeoPositionInfoSourceBb); + if (q->receivers(SIGNAL(error(QGeoPositionInfoSource::Error)))) { + Q_EMIT ((QGeoPositionInfoSource *)q)->error(_sourceError); + } else { + // user is expecting a signal to be emitted, cannot emit positionUpdated() because of + // error so emit timeout signal. The connection is queued because it is possible for the + // user to call startUpdates() from within the slot handling the timeout. The queued + // connection avoids potential infinite recursion. + Q_EMIT queuedUpdateTimeout(); + } + } +} + +// single update has timed out. This is a slot for the requestUpdate timer +void QGeoPositionInfoSourceBbPrivate::singleUpdateTimeout() +{ + _requestUpdateInvoked = false; + + emitUpdateTimeout(); + + if (!_requestUpdateInvoked) { + // close the pps file to ensure readyRead() does not spin in the event that there are + // unexpected replies that we don't read. Note that open() is done lazily in sendRequest(). + _singleUpdatePpsObject->close(); + } +} + +// This slot is intended for queued connection to the signal queuedUpdateTimeout(). If an error +// occurs when an update is requested the error is relayed via updateTimeout() but the connection +// is queued to avoid potential infinite recursion. +void QGeoPositionInfoSourceBbPrivate::emitUpdateTimeout() +{ + Q_Q(QGeoPositionInfoSourceBb); + Q_EMIT q->updateTimeout(); +} + +void QGeoPositionInfoSourceBbPrivate::emitPositionUpdated(const QGeoPositionInfo &update) +{ + // having successfully received a position update, set _canEmitPeriodicUpdatesTimeout to true, + // which (re)enables a timeout to be emitted upon any subsequent error in periodic updating. + _canEmitPeriodicUpdatesTimeout = true; + + Q_Q(QGeoPositionInfoSourceBb); + Q_EMIT q->positionUpdated(update); +} + +bool QGeoPositionInfoSourceBbPrivate::receivePositionReply(bb::PpsObject &ppsObject) +{ + QVariantMap reply; + // receiveReply() tests for errors associated with the request being replied to + if (!receiveReply(&reply, ppsObject)) { + _replyErrorCode = bb::location::PositionErrorCode::FatalUnknown; + + // if there is an error from Location Manager report it so user can access it through the + // properties when responding to the updateTimeout() signal. + if (reply.contains("errCode")) { + int errCode = reply.value("errCode").toInt(); + _replyErrorCode + = intToPositionErrorCodeMap.value(errCode, + bb::location::PositionErrorCode::FatalUnknown); + if (fatalError(_replyErrorCode)) { + _sourceError + = positionErrorCodeToErrorMap.value(_replyErrorCode, + QGeoPositionInfoSource::UnknownSourceError); + } + + if (reply.contains("err")) { + _replyErr = reply.value("err").toString(); + if (reply.contains("errstr")) { + _replyErrStr = reply.value("errstr").toString(); + } + } + } else { + _sourceError = QGeoPositionInfoSource::UnknownSourceError; + } + return false; + } + + // clear any errors + _replyErrorCode = bb::location::PositionErrorCode::None; + _replyErr = QString(); + _replyErrStr = QString(); + + // check that this is a location reply (could be a reply to another request type, eg. cancel, + // which is ignored here) + if (reply.contains("res") && reply.value("res").toString() == "location") { + // keep the raw LM reply for access via Qt properties. + _replyDat = reply.value("dat").toMap(); + + // extract the geo position info from the reply into _currentPosition + if (populatePositionInfo(&_currentPosition, reply)) { + emitPositionUpdated(_currentPosition); + } + } + + return true; +} + +void QGeoPositionInfoSourceBbPrivate::receivePeriodicPositionReply() +{ + // don't try to receive a reply if periodic updates have not been started. This is + // necessary because this slot is connected to PpsObject::readyRead() and could be + // invoked any time the pps file is updated by the server. Under error conditions + // this would otherwise lead to a circular calling sequence: receive, timeout due to + // error, cancel, receive... + if (!_startUpdatesInvoked) + return; + + if (!receivePositionReply(*_periodicUpdatePpsObject)) { + Q_Q(QGeoPositionInfoSourceBb); + if (fatalError(_replyErrorCode) + && q->receivers(SIGNAL(error(QGeoPositionInfoSource::Error)))) { + Q_EMIT ((QGeoPositionInfoSource *)q)->error(_sourceError); + } else { + periodicUpdatesTimeout(); + } + } +} + +void QGeoPositionInfoSourceBbPrivate::receiveSinglePositionReply() +{ + // don't try to receive a reply if a single update has not been requested. This is + // necessary because this slot is connected to PpsObject::readyRead() and could be + // invoked any time the pps file is updated by the server. Under error conditions + // this would otherwise lead to a circular calling sequence: receive, timeout due to + // error, cancel, receive... + if (!_requestUpdateInvoked) + return; + + // clear this before calling receivePositionReply() which can emit the positionUpdated() + // signal. It is possible to call requestUpdate() in the slot connected to + // positionUpdated() so for requestUpdate() to work _requestUpdateInvoked must be false + _requestUpdateInvoked = false; + + if (!receivePositionReply(*_singleUpdatePpsObject)) { + Q_Q(QGeoPositionInfoSourceBb); + if (fatalError(_replyErrorCode) + && q->receivers(SIGNAL(error(QGeoPositionInfoSource::Error)))) { + Q_EMIT ((QGeoPositionInfoSource *)q)->error(_sourceError); + } else { + singleUpdateTimeout(); + } + } + + if (!_requestUpdateInvoked) { + // close the pps file to ensure readyRead() does not spin in the event that there are + // unexpected replies that we don't read. Note that open() is done lazily in sendRequest(). + _singleUpdatePpsObject->close(); + } +} + +/////////////////////////// +// +// QGeoPositionInfoSourceBb +// +/////////////////////////// + +/*! + \class QGeoPositionInfoSourceBb + \brief The QGeoPositionInfoSourceBb class is for the distribution of positional updates obtained + from the underlying Qnx Location Manager. + + QGeoPositionInfoSourceBb is a subclass of QGeoPositionInfoSource. The static function + QGeoPositionInfoSource::createDefaultSource() creates a default + position source that is appropriate for the platform, if one is available. On BB10 this is + a QGeoPositionInfoSourceBb instance. + + Users of a QGeoPositionInfoSource subclass can request the current position using + requestUpdate(), or start and stop regular position updates using + startUpdates() and stopUpdates(). When an update is available, + positionUpdated() is emitted. The last known position can be accessed with + lastKnownPosition(). + + If regular position updates are required, setUpdateInterval() can be used + to specify how often these updates should be emitted. If no interval is + specified, updates are simply provided whenever they are available. + For example: + + \code + // Emit updates every 10 seconds if available + QGeoPositionInfoSource *source = QGeoPositionInfoSource::createDefaultSource(0); + if (source) + source->setUpdateInterval(10000); + \endcode + + To remove an update interval that was previously set, call + setUpdateInterval() with a value of 0. + + Note that the position source may have a minimum value requirement for + update intervals, as returned by minimumUpdateInterval(). +*/ + +/*! + Constructs a QGeoPositionInfoSourceBb instance with the given \a parent + and \a updateMode. +*/ +QGeoPositionInfoSourceBb::QGeoPositionInfoSourceBb(QObject *parent) + : QGeoPositionInfoSource(parent), + d_ptr(new QGeoPositionInfoSourceBbPrivate(this)) +{ +} + +/*! + Destroys the position source. +*/ +QGeoPositionInfoSourceBb::~QGeoPositionInfoSourceBb() +{ +} + +/*! + \reimp +*/ +void QGeoPositionInfoSourceBb::setUpdateInterval(int msec) +{ + int interval = msec; + if (interval != 0) + interval = qMax(msec, minimumUpdateInterval()); + + if (interval == updateInterval()) + return; + + QGeoPositionInfoSource::setUpdateInterval(interval); + + Q_D(QGeoPositionInfoSourceBb); + if (d->_startUpdatesInvoked) { + d->stopUpdates(); + d->startUpdates(); + } +} + +/*! + \reimp +*/ +void QGeoPositionInfoSourceBb::setPreferredPositioningMethods(PositioningMethods methods) +{ + PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods(); + QGeoPositionInfoSource::setPreferredPositioningMethods(methods); + if (previousPreferredPositioningMethods == preferredPositioningMethods()) + return; + + Q_D(QGeoPositionInfoSourceBb); + if (d->_startUpdatesInvoked) { + d->stopUpdates(); + d->startUpdates(); + } +} + +/*! + \reimp +*/ +void QGeoPositionInfoSourceBb::startUpdates() +{ + Q_D(QGeoPositionInfoSourceBb); + d->startUpdates(); +} + +/*! + \reimp +*/ +void QGeoPositionInfoSourceBb::stopUpdates() +{ + Q_D(QGeoPositionInfoSourceBb); + d->stopUpdates(); +} + +/*! + \reimp +*/ +void QGeoPositionInfoSourceBb::requestUpdate(int msec) +{ + Q_D(QGeoPositionInfoSourceBb); + d->requestUpdate(msec); +} + +/*! + \reimp +*/ +QGeoPositionInfo + QGeoPositionInfoSourceBb::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->lastKnownPosition(fromSatellitePositioningMethodsOnly); +} + +/*! + \reimp +*/ +QGeoPositionInfoSource::PositioningMethods + QGeoPositionInfoSourceBb::supportedPositioningMethods() const +{ + return AllPositioningMethods; +} + +/*! + \reimp +*/ +int QGeoPositionInfoSourceBb::minimumUpdateInterval() const +{ + return global::minUpdateInterval; +} + +QGeoPositionInfoSource::Error QGeoPositionInfoSourceBb::error() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_sourceError; +} + +// these properties extend QGeoPositionInfoSource allowing use of additional features of the Qnx Location Manager + +// the following properties are the fields of the dat parameter of the location request + +/*! + \property QGeoPositionInfoSourceBb::period + \brief This property specifies the period of the location request, in seconds. A value of + '0' indicates a one-time location request. +*/ +double QGeoPositionInfoSourceBb::period() const +{ + // convert from msec to sec + return updateInterval() / 1000.0; +} + +void QGeoPositionInfoSourceBb::setPeriod(double period) +{ + // convert from sec to msec, round to nearest msec + setUpdateInterval(qRound(static_cast<qreal>(period * 1000.0))); +} + +/*! + \property QGeoPositionInfoSourceBb::accuracy + \brief This property specifies the desired accuracy of the fix, in meters. A value of '0' + disables accuracy criteria. +*/ +double QGeoPositionInfoSourceBb::accuracy() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_accuracy; +} + +void QGeoPositionInfoSourceBb::setAccuracy(double accuracy) +{ + Q_D(QGeoPositionInfoSourceBb); + d->_accuracy = accuracy; +} + +/*! + \property QGeoPositionInfoSourceBb::responseTime + \brief This property specifies the desired response time of the fix, in seconds. A value of + '0' disables response time criteria. +*/ +double QGeoPositionInfoSourceBb::responseTime() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_responseTime; +} + +void QGeoPositionInfoSourceBb::setResponseTime(double responseTime) +{ + Q_D(QGeoPositionInfoSourceBb); + d->_responseTime = responseTime; +} + +/*! + \property QGeoPositionInfoSourceBb::canRunInBackground + \brief This property determines whether or not requests are allowed to run with the device + in standby (i.e. screen off) +*/ +bool QGeoPositionInfoSourceBb::canRunInBackground() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_canRunInBackground; +} + +void QGeoPositionInfoSourceBb::setCanRunInBackground(bool canRunInBackground) +{ + Q_D(QGeoPositionInfoSourceBb); + d->_canRunInBackground = canRunInBackground; +} + +/*! + \property QGeoPositionInfoSourceBb::provider + \brief This property specifies the location provider you wish to use (hybrid, gnss, + network). +*/ +QString QGeoPositionInfoSourceBb::provider() const +{ + return positioningMethodsToProviderMap.value(preferredPositioningMethods()); +} + +void QGeoPositionInfoSourceBb::setProvider(const QString &provider) +{ + setPreferredPositioningMethods(positioningMethodsToProviderMap.key(provider)); +} + +/*! + \property QGeoPositionInfoSourceBb::fixType + \brief This property specifies the fix type you wish to use (best, gps_ms_based, + gps_ms_assisted, gps_autonomous, cellsite, wifi). +*/ +QString QGeoPositionInfoSourceBb::fixType() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_fixType; +} + +void QGeoPositionInfoSourceBb::setFixType(const QString &fixType) +{ + Q_D(QGeoPositionInfoSourceBb); + d->_fixType = fixType; +} + +/*! + \property QGeoPositionInfoSourceBb::appId + \brief This property specifies a special application id. +*/ +QString QGeoPositionInfoSourceBb::appId() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_appId; +} + +void QGeoPositionInfoSourceBb::setAppId(const QString &appId) +{ + Q_D(QGeoPositionInfoSourceBb); + d->_appId = appId; +} + +/*! + \property QGeoPositionInfoSourceBb::appPassword + \brief This property specifies a special application password, goes with appId above. +*/ +QString QGeoPositionInfoSourceBb::appPassword() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_appPassword; +} + +void QGeoPositionInfoSourceBb::setAppPassword(const QString &appPassword) +{ + Q_D(QGeoPositionInfoSourceBb); + d->_appPassword = appPassword; +} + +/*! + \property QGeoPositionInfoSourceBb::pdeUrl + \brief This property specifies the PDE URL (i.e. tcp://user:pass@address.dom:1234). +*/ +QUrl QGeoPositionInfoSourceBb::pdeUrl() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_pdeUrl; +} + +void QGeoPositionInfoSourceBb::setPdeUrl(const QUrl &pdeUrl) +{ + Q_D(QGeoPositionInfoSourceBb); + d->_pdeUrl = pdeUrl; +} + +/*! + \property QGeoPositionInfoSourceBb::slpUrl + \brief This property specifies the SLP URL (i.e. tcp://user:pass@address.dom:1234). +*/ +QUrl QGeoPositionInfoSourceBb::slpUrl() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_slpUrl; +} + +void QGeoPositionInfoSourceBb::setSlpUrl(const QUrl &slpUrl) +{ + Q_D(QGeoPositionInfoSourceBb); + d->_slpUrl = slpUrl; +} + +// the following read-only properties are the fields of the Location Manager generic reply + +/*! + \property QGeoPositionInfoSourceBb::replyDat + \brief This property specifies the object containing the reply data (such as latitude, + longitude, satellites, etc). If the replyErr is not empty then replyDat may be empty or + stale. replyDat is expected to be consumed in the slot connected to the positionUpdated() + signal, otherwise its contents are undefined. +*/ +QVariantMap QGeoPositionInfoSourceBb::replyDat() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_replyDat; +} + +/*! + \property QGeoPositionInfoSourceBb::replyErrorCode + \brief If not empty this property indicates that an error has occurred, and identifies the + error. + + Possible values are: + + Code | Description + None | No error + FatalDisabled | Location services is disabled + FatalNoLastKnownPosition | There is no last known position on the device + FatalInsufficientProviders | There are insufficient available location technology providers + to process your request + FatalInvalidRequest | One or more of the request parameters are invalid. + WarnTimeout | A timeout has occurred while processing your request. The request will + continue until your location is obtained + WarnLostTracking | The location fix has been lost due to insufficient coverage. The request + will continue until your location is reacquired + WarnStationary | The device is stationary. No further updates until the device resumes + movement + + replyErrorCode is expected to be consumed in the slot connected to the updateTimeout() + signal, which is emitted when an error is detected. Otherwise its contents are undefined. +*/ +bb::location::PositionErrorCode::Type QGeoPositionInfoSourceBb::replyErrorCode() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_replyErrorCode; +} + +/*! + \property QGeoPositionInfoSourceBb::replyErr + \brief If not empty this property indicates that an error has occurred, and identifies the + error. replyErr is expected to be consumed in the slot connected to the updateTimeout() + signal, which is emitted when an error is detected. Otherwise its contents are undefined. +*/ +QString QGeoPositionInfoSourceBb::replyErr() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_replyErr; +} + +/*! + \property QGeoPositionInfoSourceBb::replyErrStr + \brief This property is not empty if and only if the replyErr parameter is present, it + describes the error. replyErrStr is expected to be consumed in the slot connected to the + updateTimeout() signal, which is emitted when an error is detected. Otherwise its contents + are undefined. +*/ +QString QGeoPositionInfoSourceBb::replyErrStr() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_replyErrStr; +} + +/*! + \property QGeoPositionInfoSourceBb::locationServicesEnabled + \brief This property indicates whether the location services are enabled or not. If + location services are disabled then no position updates will occur. The user must enable + location services through the Settings app before any position updates will be available. +*/ +bool QGeoPositionInfoSourceBb::locationServicesEnabled() const +{ + bool locationEnabled = false; + if (location_manager_get_status(&locationEnabled) != 0) { + qWarning() << "QGeoPositionInfoSourceBb::locationServicesEnabled() : " + "call to location_manager_get_status() failed."; + } + return locationEnabled; +} + +/*! + \property QGeoPositionInfoSourceBb::reset + \brief By setting this property a reset of all location providers is requested through the + Location Manager. The value of reset specifies the type of reset to be performed. Valid + reset types are "cold", "warm", "hot", and "factory". The reset is not actually carried out + until position updates are restarted. The current value of this property, i.e. + property("reset"), is not particularly useful, it is simply the reset type corresponding to + the last time setProperty("reset", resetType) was called. A Qt property must have a READ + method, hence the reason for defining resetType(). +*/ +QString QGeoPositionInfoSourceBb::resetType() const +{ + Q_D(const QGeoPositionInfoSourceBb); + return d->_resetType; +} + +void QGeoPositionInfoSourceBb::requestReset(const QString &resetType) +{ + if (validResetTypes.contains(resetType)) { + Q_D(QGeoPositionInfoSourceBb); + d->_resetType = resetType; + d->resetLocationProviders(); + } +} diff --git a/src/plugins/position/blackberry/qgeopositioninfosource_bb.h b/src/plugins/position/blackberry/qgeopositioninfosource_bb.h new file mode 100644 index 00000000..f142dc40 --- /dev/null +++ b/src/plugins/position/blackberry/qgeopositioninfosource_bb.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCE_BB_H +#define QGEOPOSITIONINFOSOURCE_BB_H + +#include <QGeoPositionInfoSource> + +#include <bb/location/PositionErrorCode> + +#include <QString> +#include <QVariantMap> +#include <QScopedPointer> +#include <QUrl> + +class QGeoPositionInfoSourceBbPrivate; +class BB_LOCATION_EXPORT QGeoPositionInfoSourceBb : public QGeoPositionInfoSource +{ + Q_OBJECT + + Q_PROPERTY(double period READ period WRITE setPeriod FINAL) + Q_PROPERTY(double accuracy READ accuracy WRITE setAccuracy FINAL) + Q_PROPERTY(double responseTime READ responseTime WRITE setResponseTime FINAL) + Q_PROPERTY(bool canRunInBackground READ canRunInBackground WRITE setCanRunInBackground FINAL) + Q_PROPERTY(QString provider READ provider WRITE setProvider FINAL) + Q_PROPERTY(QString fixType READ fixType WRITE setFixType FINAL) + Q_PROPERTY(QString appId READ appId WRITE setAppId FINAL) + Q_PROPERTY(QString appPassword READ appPassword WRITE setAppPassword FINAL) + Q_PROPERTY(QUrl pdeUrl READ pdeUrl WRITE setPdeUrl FINAL) + Q_PROPERTY(QUrl slpUrl READ slpUrl WRITE setSlpUrl FINAL) + + Q_PROPERTY(QVariantMap replyDat READ replyDat FINAL) + Q_PROPERTY(bb::location::PositionErrorCode::Type replyErrorCode READ replyErrorCode FINAL) + Q_PROPERTY(QString replyErr READ replyErr FINAL) + Q_PROPERTY(QString replyErrStr READ replyErrStr FINAL) + + Q_PROPERTY(bool locationServicesEnabled READ locationServicesEnabled FINAL) + + Q_PROPERTY(QString reset READ resetType WRITE requestReset FINAL) + +public: + explicit QGeoPositionInfoSourceBb(QObject *parent = 0); + virtual ~QGeoPositionInfoSourceBb(); + + void setUpdateInterval(int msec); + void setPreferredPositioningMethods(PositioningMethods methods); + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; + PositioningMethods supportedPositioningMethods() const; + int minimumUpdateInterval() const; + Error error() const; + + double period() const; + void setPeriod(double period); + + double accuracy() const; + void setAccuracy(double accuracy); + + double responseTime() const; + void setResponseTime(double responseTime); + + bool canRunInBackground() const; + void setCanRunInBackground(bool canRunInBackground); + + QString provider() const; + void setProvider(const QString &provider); + + QString fixType() const; + void setFixType(const QString &fixType); + + QString appId() const; + void setAppId(const QString &appId); + + QString appPassword() const; + void setAppPassword(const QString &appPassword); + + QUrl pdeUrl() const; + void setPdeUrl(const QUrl &pdeUrl); + + QUrl slpUrl() const; + void setSlpUrl(const QUrl &slpUrl); + + QVariantMap replyDat() const; + + bb::location::PositionErrorCode::Type replyErrorCode() const; + + QString replyErr() const; + + QString replyErrStr() const; + + bool locationServicesEnabled() const; + + QString resetType() const; + void requestReset(const QString &resetType); + + +public Q_SLOTS: + void startUpdates(); + void stopUpdates(); + void requestUpdate(int timeout = 0); + +private: + Q_DECLARE_PRIVATE(QGeoPositionInfoSourceBb) + Q_DISABLE_COPY(QGeoPositionInfoSourceBb) + QScopedPointer<QGeoPositionInfoSourceBbPrivate> d_ptr; +}; + +#endif diff --git a/src/plugins/position/blackberry/qgeopositioninfosource_bb_p.h b/src/plugins/position/blackberry/qgeopositioninfosource_bb_p.h new file mode 100644 index 00000000..918d4913 --- /dev/null +++ b/src/plugins/position/blackberry/qgeopositioninfosource_bb_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEBB_P_H +#define QGEOPOSITIONINFOSOURCEBB_P_H + +#include "qgeopositioninfosource_bb.h" +#include "qgeopositioninfo.h" + +#include <bb/location/PositionErrorCode> + +#include <QObject> +#include <QVariantMap> +#include <QUrl> + +namespace bb +{ +class PpsObject; +} + +class QGeoPositionInfoSourceBbPrivate : public QObject +{ + Q_OBJECT +public: + ~QGeoPositionInfoSourceBbPrivate(); + + void startUpdates(); + void stopUpdates(); + void requestUpdate(int msec); + + bool _startUpdatesInvoked; + bool _requestUpdateInvoked; + +private Q_SLOTS: + void singleUpdateTimeout(); + void receivePeriodicPositionReply(); + void receiveSinglePositionReply(); + + void emitUpdateTimeout(); + +Q_SIGNALS: + void queuedUpdateTimeout(); + +private: + Q_DECLARE_PUBLIC(QGeoPositionInfoSourceBb) + explicit QGeoPositionInfoSourceBbPrivate(QGeoPositionInfoSourceBb *parent); + + void periodicUpdatesTimeout(); + + void emitPositionUpdated(const QGeoPositionInfo &update); + bool requestPositionInfo(bool periodic, int singleRequestMsec = 0); + void cancelPositionInfo(bool periodic); + void resetLocationProviders(); + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const; + + QVariantMap populateLocationRequest(bool periodic, int singleRequestMsec = 0) const; + QVariantMap populateResetRequest() const; + + bool receivePositionReply(bb::PpsObject &ppsObject); + + bool _canEmitPeriodicUpdatesTimeout; + + QGeoPositionInfoSourceBb *q_ptr; + bb::PpsObject *_periodicUpdatePpsObject; + bb::PpsObject *_singleUpdatePpsObject; + QGeoPositionInfo _currentPosition; + QGeoPositionInfoSource::Error _sourceError; + + // these are Location Manager parameters that are represented by properties. These parameters + // represent additional functionality beyond what is provided by the base class + // QGeoPositionInfoSource + double _accuracy; + double _responseTime; + bool _canRunInBackground; + QString _fixType; + QString _appId; + QString _appPassword; + QUrl _pdeUrl; + QUrl _slpUrl; + QVariantMap _replyDat; + bb::location::PositionErrorCode::Type _replyErrorCode; + QString _replyErr; + QString _replyErrStr; + QString _resetType; + +}; + +#endif diff --git a/src/plugins/position/blackberry/qgeopositioninfosourcefactory_bb.cpp b/src/plugins/position/blackberry/qgeopositioninfosourcefactory_bb.cpp new file mode 100644 index 00000000..64a5ed9e --- /dev/null +++ b/src/plugins/position/blackberry/qgeopositioninfosourcefactory_bb.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosourcefactory_bb.h" +#include "qgeopositioninfosource_bb.h" +#include "qgeosatelliteinfosource_bb.h" + +#include <QGeoPositionInfoSource> +#include <QGeoSatelliteInfoSource> + + +/////////////////////////// +// +// QGeoPositionInfoSourceFactoryBb +// +/////////////////////////// + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryBb::positionInfoSource(QObject *parent) +{ + return new QGeoPositionInfoSourceBb(parent); +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryBb::satelliteInfoSource(QObject *parent) +{ + return new QGeoSatelliteInfoSourceBb(parent); +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryBb::areaMonitor(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} diff --git a/src/plugins/position/blackberry/qgeopositioninfosourcefactory_bb.h b/src/plugins/position/blackberry/qgeopositioninfosourcefactory_bb.h new file mode 100644 index 00000000..439c2ae6 --- /dev/null +++ b/src/plugins/position/blackberry/qgeopositioninfosourcefactory_bb.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEFACTORY_BB_H +#define QGEOPOSITIONINFOSOURCEFACTORY_BB_H + +#include <bb/Global> + +#include <QGeoPositionInfoSourceFactory> + +#include <QObject> + +class QGeoPositionInfoSourceFactoryBb : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + Q_INTERFACES(QGeoPositionInfoSourceFactory) + +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent); + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); + QGeoAreaMonitorSource *areaMonitor(QObject *parent); +}; + +#endif // include guard diff --git a/src/plugins/position/blackberry/qgeosatelliteinfosource_bb.cpp b/src/plugins/position/blackberry/qgeosatelliteinfosource_bb.cpp new file mode 100644 index 00000000..241b99a5 --- /dev/null +++ b/src/plugins/position/blackberry/qgeosatelliteinfosource_bb.cpp @@ -0,0 +1,621 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <bb/PpsObject> + +#include "qgeosatelliteinfosource_bb_p.h" +#include "locationmanagerutil_bb.h" + +#include <location_manager.h> + +extern "C" { +#include <wmm/wmm.h> +} + +#include <QVariantMap> +#include <QByteArray> +#include <QtDebug> + +#include <errno.h> + +/////////////////////////// +// +// local variables/functions +// +/////////////////////////// + +namespace global +{ + +// While waiting for a position fix a satellite update period of 1 sec is considered reasonable. +static const double defaultSatelliteUpdatePeriod = 1.0; + +} // namespace global + +namespace +{ +// map the Location Manager reply error codes to the QGeoSatelliteInfoSource::Error enum values. +QMap<int, QGeoSatelliteInfoSource::Error> createIntToErrorCodeMap() +{ + QMap<int, QGeoSatelliteInfoSource::Error> map; + + map.insert(0, QGeoSatelliteInfoSource::NoError); + map.insert(1, QGeoSatelliteInfoSource::ClosedError); + map.insert(2, QGeoSatelliteInfoSource::UnknownSourceError); + map.insert(3, QGeoSatelliteInfoSource::UnknownSourceError); + map.insert(4, QGeoSatelliteInfoSource::UnknownSourceError); + map.insert(5, QGeoSatelliteInfoSource::UnknownSourceError); + map.insert(6, QGeoSatelliteInfoSource::AccessError); + // the following are not considered errors from QGeoSatelliteInfoSource perspective + // (they are timeout conditions) + map.insert(0x10000, QGeoSatelliteInfoSource::NoError); + map.insert(0x10001, QGeoSatelliteInfoSource::NoError); + map.insert(0x10002, QGeoSatelliteInfoSource::NoError); + + return map; +} + +const QMap<int, QGeoSatelliteInfoSource::Error> intToErrorCodeMap = + createIntToErrorCodeMap(); + +QGeoSatelliteInfo::SatelliteSystem determineSatSystem(int satId) +{ + if (satId >= 1 && satId <= 32) + return QGeoSatelliteInfo::GPS; + + if (satId >= 65 && satId <= 88) + return QGeoSatelliteInfo::GLONASS; + + return QGeoSatelliteInfo::Undefined; +} + +} + +/////////////////////////// +// +// QGeoSatelliteInfoSourceBbPrivate +// +/////////////////////////// + +// Create a QVariantMap suitable for writing to a PpsObject specifying a location request to the +// Location Manager. If the request is periodic then the update interval is used. Otherwise 0 +// indicates to the Location Manager that it is a request for a single, immediate location response. +// singleRequestMsec applies only to the single, immediate location response. It represents the +// expected location response time, after which it is assumed a timeout response occurs. +QVariantMap QGeoSatelliteInfoSourceBbPrivate::populateLocationRequest(bool periodic, + int singleRequestMsec) +{ + Q_Q(const QGeoSatelliteInfoSourceBb); + + QVariantMap map; + QVariantMap datMap; + + int period; + int responseTime; + if (periodic) { + // rounding is performed here because the Location Manager truncates to nearest integer + period = (q->updateInterval() + 500) / 1000; + if (period == 0) + period = ::global::defaultSatelliteUpdatePeriod; + + responseTime = _responseTime; + } else { + period = 0; + responseTime = (singleRequestMsec + 500) / 1000; + } + + datMap.insert("period", period); + datMap.insert("accuracy", 0); + datMap.insert("response_time", responseTime); + datMap.insert("background", _backgroundMode); + + datMap.insert("provider", "gnss"); + datMap.insert("fix_type", "gps_autonomous"); + + // have the Location Manager return the satellite information even if it does not have a + // position fix. + datMap.insert("report_sat", true); + + map.insert("msg", "location"); + map.insert("id", global::libQtLocationId); + map.insert("dat", datMap); + + return map; +} + +// From a QvariantMap representing a location response from the Location Manager fill the lists of +// QGeoSatelliteInfo instances intended to be emitted via satellitesInUseUpdated() and +// satellitesInViewUpdated() signals. +void QGeoSatelliteInfoSourceBbPrivate::populateSatelliteLists(const QVariantMap &map) +{ + // populate _currentSatelliteInfo + QVariantMap datMap = map.value("dat").toMap(); + QVariantList satelliteList = datMap.value("satellites").toList(); + + _satellitesInView.clear(); + _satellitesInUse.clear(); + + Q_FOREACH (const QVariant &satelliteData, satelliteList) { + datMap = satelliteData.toMap(); + QGeoSatelliteInfo satelliteInfo = QGeoSatelliteInfo(); + + if (datMap.contains("id")) + satelliteInfo.setSatelliteIdentifier(static_cast<int>(datMap.value("id").toDouble())); + + satelliteInfo.setSatelliteSystem(determineSatSystem(satelliteInfo.satelliteIdentifier())); + + if (datMap.contains("cno")) + satelliteInfo.setSignalStrength(static_cast<int>(datMap.value("cno").toDouble())); + + // attributes + if (datMap.contains("elevation")) + satelliteInfo.setAttribute(QGeoSatelliteInfo::Elevation, + static_cast<qreal>(datMap.value("elevation").toDouble())); + else + satelliteInfo.removeAttribute(QGeoSatelliteInfo::Elevation); + + if (datMap.contains("azimuth")) + satelliteInfo.setAttribute(QGeoSatelliteInfo::Azimuth, + static_cast<qreal>(datMap.value("azimuth").toDouble())); + else + satelliteInfo.removeAttribute(QGeoSatelliteInfo::Azimuth); + + // each satellite in this list is considered "in view" + _satellitesInView.append(satelliteInfo); + + if (datMap.value("used").toBool() == true) + _satellitesInUse.append(satelliteInfo); + } +} + +// The satellite data is retrieved from a location request +bool QGeoSatelliteInfoSourceBbPrivate::requestSatelliteInfo(bool periodic, + int singleRequestMsec) +{ + // build up the request + QVariantMap request = populateLocationRequest(periodic, singleRequestMsec); + + bb::PpsObject *ppsObject; + if (periodic) + ppsObject = _periodicUpdatePpsObject; + else + ppsObject = _singleUpdatePpsObject; + + if (sendRequest(*ppsObject, request) == false) { + stopUpdates(); + +#ifndef BB_TEST_BUILD + // test for pps file error + switch (ppsObject->error()) { + case EACCES: + _sourceError = QGeoSatelliteInfoSource::AccessError; + break; + default: + _sourceError = QGeoSatelliteInfoSource::UnknownSourceError; + break; + } +#endif // !BB_TEST_BUILD + + return false; + } + + return true; +} + +void QGeoSatelliteInfoSourceBbPrivate::cancelSatelliteInfo(bool periodic) +{ + bb::PpsObject *ppsObject; + if (periodic) + ppsObject = _periodicUpdatePpsObject; + else + ppsObject = _singleUpdatePpsObject; + + (void)sendRequest(*ppsObject, global::cancelRequest); +} + +// Constructor. Note there are two PpsObjects for handling the two different types of requests that +// can be simultaneously made and which must be handled independently (apart from both being emitted +// through the same signal when done-part of Qt Mobility spec. +QGeoSatelliteInfoSourceBbPrivate::QGeoSatelliteInfoSourceBbPrivate( + QGeoSatelliteInfoSourceBb *parent) + : QObject(parent), + _startUpdatesInvoked(false), + _requestUpdateInvoked(false), + q_ptr(parent), + _periodicUpdatePpsObject(new bb::PpsObject(global::locationManagerPpsFile, this)), + _singleUpdatePpsObject(new bb::PpsObject(global::locationManagerPpsFile, this)), + _sourceError(QGeoSatelliteInfoSource::NoError), + _backgroundMode(false), + _responseTime(0) +{ + // connect to periodic update PpsObject::readyRead() + connect(_periodicUpdatePpsObject, SIGNAL(readyRead()), SLOT(receivePeriodicSatelliteReply())); + + // connect to single update PpsObject::readyRead() + connect(_singleUpdatePpsObject, SIGNAL(readyRead()), SLOT(receiveSingleSatelliteReply())); + + // queued connection to signal requestTimeout() + connect(this, SIGNAL(queuedRequestTimeout()), SLOT(emitRequestTimeout()), Qt::QueuedConnection); +} + +QGeoSatelliteInfoSourceBbPrivate::~QGeoSatelliteInfoSourceBbPrivate() +{ + stopUpdates(); +} + +// request periodic updates +void QGeoSatelliteInfoSourceBbPrivate::startUpdates() +{ + // do nothing if periodic updates have already been started + if (_startUpdatesInvoked) + return; + + // build a request and initiate it + if (requestSatelliteInfo(true)) { + _startUpdatesInvoked = true; + } else { + Q_Q(QGeoSatelliteInfoSourceBb); + Q_EMIT ((QGeoSatelliteInfoSource *)q)->error(_sourceError); + } +} + +// stop periodic updates +void QGeoSatelliteInfoSourceBbPrivate::stopUpdates() +{ + // do nothing if periodic updates have not been started + if (!_startUpdatesInvoked) + return; + + cancelSatelliteInfo(true); + _startUpdatesInvoked = false; + + // close the pps file to ensure readyRead() does not spin in the event that we don't read the + // reply to the cancel request. Note that open() is done lazily in sendRequest(). + _periodicUpdatePpsObject->close(); +} + +// request single update +void QGeoSatelliteInfoSourceBbPrivate::requestUpdate(int msec) +{ + // do nothing if an immediate update has already been requested + if (_requestUpdateInvoked) + return; + + if (msec) { + // If it is not possible to update in msec timeout immediately. + if (msec < global::minUpdateInterval) { + // The connection is queued because it is possible for the user to call requestUpdate() + // from within the slot handling the timeout. The queued connection avoids potential + // infinite recursion. + Q_EMIT queuedRequestTimeout(); + return; + } + } + + if (requestSatelliteInfo(false, msec)) { + _requestUpdateInvoked = true; + } else { + // With Qt5 the error() signal was introduced. If there are any receivers emit error() else + // maintain QtMobility behavior. + Q_Q(QGeoSatelliteInfoSourceBb); + if (q->receivers(SIGNAL(error(QGeoPositionInfoSource::Error)))) { + Q_EMIT ((QGeoSatelliteInfoSource *)q)->error(_sourceError); + } else { + // The connection is queued because it is possible for the user to call requestUpdate() + // from within the slot handling the timeout. The queued connection avoids potential + // infinite recursion. + Q_EMIT queuedRequestTimeout(); + } + } +} + +// single update has timed out. This is a slot for the requestUpdate timer +void QGeoSatelliteInfoSourceBbPrivate::singleUpdateTimeout() +{ + _requestUpdateInvoked = false; + + emitRequestTimeout(); + + if (!_requestUpdateInvoked) { + // close the pps file to ensure readyRead() does not spin in the event that there are + // unexpected replies that we don't read. Note that open() is done lazily in sendRequest(). + _singleUpdatePpsObject->close(); + } +} + +// This slot is intended for queued connection to the signal queuedUpdateTimeout(). If an error +// occurs when an update is requested the error is relayed via updateTimeout() but the connection +// is queued to avoid potential infinite recursion. +void QGeoSatelliteInfoSourceBbPrivate::emitRequestTimeout() +{ + Q_Q(QGeoSatelliteInfoSourceBb); + Q_EMIT q->requestTimeout(); +} + +bool QGeoSatelliteInfoSourceBbPrivate::receiveSatelliteReply(bool periodic) +{ + bb::PpsObject *ppsObject; + if (periodic) + ppsObject = _periodicUpdatePpsObject; + else + ppsObject = _singleUpdatePpsObject; + + QVariantMap reply; + // receiveReply() tests for errors associated with the request being replied to + if (!receiveReply(&reply, *ppsObject)) { + QGeoSatelliteInfoSource::Error fatalError = QGeoSatelliteInfoSource::UnknownSourceError; + + // if there is an error from Location Manager translate it to a + // QGeoSatelliteInfoSource::Error + if (reply.contains("errCode")) { + fatalError = intToErrorCodeMap.value(reply.value("errCode").toInt(), + QGeoSatelliteInfoSource::UnknownSourceError); + } + + if (fatalError != QGeoSatelliteInfoSource::NoError) + _sourceError = fatalError; + + if (!periodic || fatalError != QGeoSatelliteInfoSource::NoError) + return false; + } + + // check that this is a location reply (could be a reply to another request type, eg. cancel, + // which is ignored here) + if (reply.contains("res") && reply.value("res").toString() == "location") { + // extract the satellite info from the reply into _satellitesInView and _satellitesInUse + populateSatelliteLists(reply); + + Q_Q(QGeoSatelliteInfoSourceBb); + Q_EMIT q->satellitesInUseUpdated(_satellitesInUse); + Q_EMIT q->satellitesInViewUpdated(_satellitesInView); + } + + return true; +} + +void QGeoSatelliteInfoSourceBbPrivate::receivePeriodicSatelliteReply() +{ + // don't try to receive a reply if periodic updates have not been started. This is + // necessary because this slot is connected to PpsObject::readyRead() and could be + // invoked any time the pps file is updated by the server. Under error conditions + // this would otherwise lead to a circular calling sequence: receive, timeout due to + // error, cancel, receive... + if (!_startUpdatesInvoked) + return; + + if (!receiveSatelliteReply(true)) { + Q_Q(QGeoSatelliteInfoSourceBb); + Q_EMIT ((QGeoSatelliteInfoSource *)q)->error(_sourceError); + } +} + +void QGeoSatelliteInfoSourceBbPrivate::receiveSingleSatelliteReply() +{ + // don't try to receive a reply if a single update has not been requested. This is + // necessary because this slot is connected to PpsObject::readyRead() and could be + // invoked any time the pps file is updated by the server. Under error conditions + // this would otherwise lead to a circular calling sequence: receive, timeout due to + // error, cancel, receive... + if (!_requestUpdateInvoked) + return; + + // clear this before calling receivePositionReply() which can emit the positionUpdated() + // signal. It is possible to call requestUpdate() in the slot connected to + // positionUpdated() so for requestUpdate() to work _requestUpdateInvoked must be false + _requestUpdateInvoked = false; + + if (!receiveSatelliteReply(false)) { + Q_Q(QGeoSatelliteInfoSourceBb); + if (_sourceError != QGeoSatelliteInfoSource::NoError) + Q_EMIT ((QGeoSatelliteInfoSource *)q)->error(_sourceError); + else + singleUpdateTimeout(); + } + + if (!_requestUpdateInvoked) { + // close the pps file to ensure readyRead() does not spin in the event that there are + // unexpected replies that we don't read. Note that open() is done lazily in sendRequest(). + _singleUpdatePpsObject->close(); + } +} + +/////////////////////////// +// +// QGeoSatelliteInfoSourceBb +// +/////////////////////////// + +/*! + \class QGeoSatelliteInfoSourceBb + \brief The QGeoSatelliteInfoSourceBb class is for the distribution of positional updates + obtained from the underlying Qnx Location Manager. + + QGeoSatelliteInfoSourceBb is a subclass of QGeoSatelliteInfoSource. + The static function QGeoSatelliteInfoSource::createDefaultSource() creates a default + satellite data source that is appropriate for the platform, if one is + available. On BB10 this is a QGeoSatelliteInfoSourceBb instance. Otherwise, available + QGeoPositionInfoSourceFactory plugins will + be checked for one that has a satellite data source available. + + Call startUpdates() and stopUpdates() to start and stop regular updates, + or requestUpdate() to request a single update. + When an update is available, satellitesInViewUpdated() and/or + satellitesInUseUpdated() will be emitted. +*/ + + + +/*! + Constructs a QGeoSatelliteInfoSourceBb instance with the given \a parent + and \a updateMode. +*/ +QGeoSatelliteInfoSourceBb::QGeoSatelliteInfoSourceBb(QObject *parent) + : QGeoSatelliteInfoSource(parent), + d_ptr(new QGeoSatelliteInfoSourceBbPrivate(this)) +{ +} + +/*! + Destroys the satellite source. +*/ +QGeoSatelliteInfoSourceBb::~QGeoSatelliteInfoSourceBb() +{ +} + +/*! + \reimp +*/ +int QGeoSatelliteInfoSourceBb::minimumUpdateInterval() const +{ + return global::minUpdateInterval; +} + +/*! + \reimp +*/ +void QGeoSatelliteInfoSourceBb::setUpdateInterval(int msec) +{ + int interval = msec; + if (interval != 0) + interval = qMax(msec, minimumUpdateInterval()); + + if (interval == updateInterval()) + return; + + QGeoSatelliteInfoSource::setUpdateInterval(interval); + + Q_D(QGeoSatelliteInfoSourceBb); + if (d->_startUpdatesInvoked) { + d->stopUpdates(); + d->startUpdates(); + } +} + +/*! + \reimp +*/ +void QGeoSatelliteInfoSourceBb::startUpdates() +{ + Q_D(QGeoSatelliteInfoSourceBb); + d->startUpdates(); +} + +/*! + \reimp +*/ +void QGeoSatelliteInfoSourceBb::stopUpdates() +{ + Q_D(QGeoSatelliteInfoSourceBb); + d->stopUpdates(); +} + +/*! + \reimp +*/ +void QGeoSatelliteInfoSourceBb::requestUpdate(int msec) +{ + Q_D(QGeoSatelliteInfoSourceBb); + d->requestUpdate(msec); +} + +/*! + \reimp +*/ +QGeoSatelliteInfoSource::Error QGeoSatelliteInfoSourceBb::error() const +{ + Q_D(const QGeoSatelliteInfoSourceBb); + return d->_sourceError; +} + +// property managers. These properties extend QGeoSatelliteInfoSource by allowing additional +// control provided by the Location Manager + +/*! + \property QGeoSatelliteInfoSourceBb::period + \brief The period of the location request, in seconds. A value of '0' indicates that this + would be a one-time location request. +*/ +double QGeoSatelliteInfoSourceBb::period() const +{ + // convert from msec to sec + return updateInterval() / 1000.0; +} + +void QGeoSatelliteInfoSourceBb::setPeriod(double period) +{ + // convert from sec to msec, round to nearest msec + setUpdateInterval(qRound(static_cast<qreal>(period * 1000.0))); +} + + +/*! + \property QGeoSatelliteInfoSourceBb::backgroundMode + \brief This property determines whether or not requests are allowed to run with the device + in standby (i.e. screen off) +*/ +bool QGeoSatelliteInfoSourceBb::backgroundMode() const +{ + Q_D(const QGeoSatelliteInfoSourceBb); + return d->_backgroundMode; +} + +void QGeoSatelliteInfoSourceBb::setBackgroundMode(bool mode) +{ + Q_D(QGeoSatelliteInfoSourceBb); + d->_backgroundMode = mode; +} + +/*! + \property QGeoSatelliteInfoSourceBb::responseTime + \brief This property specifies the desired response time of the fix, in seconds. A value + of '0' disables response time criteria. +*/ +int QGeoSatelliteInfoSourceBb::responseTime() const +{ + Q_D(const QGeoSatelliteInfoSourceBb); + return d->_responseTime; +} + +void QGeoSatelliteInfoSourceBb::setResponseTime(int responseTime) +{ + Q_D(QGeoSatelliteInfoSourceBb); + d->_responseTime = responseTime; +} diff --git a/src/plugins/position/blackberry/qgeosatelliteinfosource_bb.h b/src/plugins/position/blackberry/qgeosatelliteinfosource_bb.h new file mode 100644 index 00000000..975ae3a1 --- /dev/null +++ b/src/plugins/position/blackberry/qgeosatelliteinfosource_bb.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSATELLITEINFOSOURCE_BB_H +#define QGEOSATELLITEINFOSOURCE_BB_H + +#include <QGeoSatelliteInfoSource> + +#include <QString> +#include <QScopedPointer> + +class QGeoSatelliteInfoSourceBbPrivate; +class QGeoSatelliteInfoSourceBb : public QGeoSatelliteInfoSource +{ + Q_OBJECT + + Q_PROPERTY(double period READ period WRITE setPeriod FINAL) + Q_PROPERTY(bool backgroundMode READ backgroundMode WRITE setBackgroundMode FINAL) + Q_PROPERTY(int responseTime READ responseTime WRITE setResponseTime FINAL) + +public: + explicit QGeoSatelliteInfoSourceBb(QObject *parent = 0); + virtual ~QGeoSatelliteInfoSourceBb(); + + void setUpdateInterval(int msec); + int minimumUpdateInterval() const; + Error error() const; + + double period() const; + void setPeriod(double period); + + bool backgroundMode() const; + void setBackgroundMode(bool mode); + + int responseTime() const; + void setResponseTime(int responseTime); + +public Q_SLOTS: + void startUpdates(); + void stopUpdates(); + void requestUpdate(int timeout = 0); + +private: + Q_DECLARE_PRIVATE(QGeoSatelliteInfoSourceBb) + Q_DISABLE_COPY(QGeoSatelliteInfoSourceBb) + QScopedPointer<QGeoSatelliteInfoSourceBbPrivate> d_ptr; +}; + +#endif diff --git a/src/plugins/position/blackberry/qgeosatelliteinfosource_bb_p.h b/src/plugins/position/blackberry/qgeosatelliteinfosource_bb_p.h new file mode 100644 index 00000000..63eb94b2 --- /dev/null +++ b/src/plugins/position/blackberry/qgeosatelliteinfosource_bb_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 - 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSATELLITEINFOSOURCE_BB_P_H +#define QGEOSATELLITEINFOSOURCE_BB_P_H + +#include "qgeosatelliteinfosource_bb.h" +#include "qgeosatelliteinfo.h" + +#include <QObject> +#include <QtCore/QVariantMap> + +namespace bb +{ +class PpsObject; +} + +class QGeoSatelliteInfoSourceBbPrivate : public QObject +{ + Q_OBJECT +public: + ~QGeoSatelliteInfoSourceBbPrivate(); + + void startUpdates(); + void stopUpdates(); + void requestUpdate(int msec); + + bool _startUpdatesInvoked; + bool _requestUpdateInvoked; + +private Q_SLOTS: + void singleUpdateTimeout(); + void receivePeriodicSatelliteReply(); + void receiveSingleSatelliteReply(); + + void emitRequestTimeout(); + +Q_SIGNALS: + void queuedRequestTimeout(); + +private: + Q_DECLARE_PUBLIC(QGeoSatelliteInfoSourceBb) + explicit QGeoSatelliteInfoSourceBbPrivate(QGeoSatelliteInfoSourceBb *parent); + + void emitSatelliteUpdated(const QGeoSatelliteInfo &update); + bool requestSatelliteInfo(bool periodic, int singleRequestMsec = 0); + void cancelSatelliteInfo(bool periodic); + + QVariantMap populateLocationRequest(bool periodic, int singleRequestMsec = 0); + void populateSatelliteLists(const QVariantMap &reply); + + bool receiveSatelliteReply(bool periodic); + + QGeoSatelliteInfoSourceBb *q_ptr; + bb::PpsObject *_periodicUpdatePpsObject; + bb::PpsObject *_singleUpdatePpsObject; + QList<QGeoSatelliteInfo> _satellitesInUse; + QList<QGeoSatelliteInfo> _satellitesInView; + QGeoSatelliteInfoSource::Error _sourceError; + + // properties (extension of QGeoSatelliteInfoSource for additional Location Manager features) + bool _backgroundMode; + int _responseTime; + +}; + +#endif diff --git a/src/plugins/position/position.pro b/src/plugins/position/position.pro index c73ba9e1..af2a5009 100644 --- a/src/plugins/position/position.pro +++ b/src/plugins/position/position.pro @@ -3,6 +3,7 @@ TEMPLATE = subdirs config_geoclue:SUBDIRS += geoclue config_gypsy:SUBDIRS += gypsy simulator:SUBDIRS += simulator +blackberry:SUBDIRS += blackberry SUBDIRS += \ positionpoll |