From ccd19d2b7ee4184503ea46b98333b27a5613190e Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Wed, 8 May 2013 18:04:31 +0200 Subject: big change: a new abstract BluetoothWatch class --- libsowatchbt/bluetoothwatch.cpp | 176 +++++++++++++++++++++++++++++++++ libsowatchbt/bluetoothwatch.h | 74 ++++++++++++++ libsowatchbt/bluetoothwatchscanner.cpp | 31 ++++++ libsowatchbt/bluetoothwatchscanner.h | 37 +++++++ libsowatchbt/libsowatchbt.pro | 38 +++++++ libsowatchbt/sowatchbt.h | 9 ++ libsowatchbt/sowatchbt_global.h | 12 +++ 7 files changed, 377 insertions(+) create mode 100644 libsowatchbt/bluetoothwatch.cpp create mode 100644 libsowatchbt/bluetoothwatch.h create mode 100644 libsowatchbt/bluetoothwatchscanner.cpp create mode 100644 libsowatchbt/bluetoothwatchscanner.h create mode 100644 libsowatchbt/libsowatchbt.pro create mode 100644 libsowatchbt/sowatchbt.h create mode 100644 libsowatchbt/sowatchbt_global.h (limited to 'libsowatchbt') diff --git a/libsowatchbt/bluetoothwatch.cpp b/libsowatchbt/bluetoothwatch.cpp new file mode 100644 index 0000000..d977b67 --- /dev/null +++ b/libsowatchbt/bluetoothwatch.cpp @@ -0,0 +1,176 @@ +#include "bluetoothwatch.h" + +using namespace sowatch; +QTM_USE_NAMESPACE + +const int BluetoothWatch::connectRetryTimes[] = { + 5, 10, 30, 60, 120, 300 +}; + +BluetoothWatch::BluetoothWatch(const QBluetoothAddress& address, QObject *parent) + : Watch(parent), + _localDev(new QBluetoothLocalDevice(this)), + _address(address), + _socket(0), + _connected(false), + _connectRetries(0), + _connectTimer(new QTimer(this)), + _connectAlignedTimer(new QSystemAlignedTimer(this)) +{ + _connectTimer->setSingleShot(true); + _connectAlignedTimer->setSingleShot(true); + + connect(_connectTimer, SIGNAL(timeout()), SLOT(handleConnectTimer())); + connect(_connectAlignedTimer, SIGNAL(timeout()), SLOT(handleConnectTimer())); + connect(_localDev, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode)), + SLOT(handleLocalDevModeChanged(QBluetoothLocalDevice::HostMode))); + + // Check to see if we can connect right away + if (_localDev->isValid() && + _localDev->hostMode() != QBluetoothLocalDevice::HostPoweredOff) { + // Do an initial connection attempt after a short delay + // (To give time for other plugins to initialize, etc.) + scheduleConnect(); + } else { + qDebug() << "Not starting MetaWatch connection because BT is off"; + } +} + +BluetoothWatch::~BluetoothWatch() +{ + if (_socket) { + _socket->close(); + delete _socket; + } +} + +bool BluetoothWatch::isConnected() const +{ + return _connected; +} + +void BluetoothWatch::scheduleConnect() +{ + if (_connected || + _connectAlignedTimer->isActive() || _connectTimer->isActive()) { + // Already connected or already scheduled to connect. + return; + } + + _connectRetries = 0; + _connectTimer->start(100); +} + +void BluetoothWatch::scheduleRetryConnect() +{ + if (_connected || + _connectAlignedTimer->isActive() || _connectTimer->isActive()) { + // Already connected or already scheduled to connect. + return; + } + + int timeToNextRetry; + if (_connectRetries >= connectRetryTimesSize) { + timeToNextRetry = connectRetryTimes[connectRetryTimesSize - 1]; + } else { + timeToNextRetry = connectRetryTimes[_connectRetries]; + _connectRetries++; // Increase the number of connection attemps + } + + qDebug() << "Backing off for" << timeToNextRetry << "seconds for next retry"; + _connectAlignedTimer->start(timeToNextRetry / 2, timeToNextRetry * 2); + if (_connectAlignedTimer->lastError() != QSystemAlignedTimer::NoError) { + // Hopefully a future version of QSystemAlignedTimer implements this fallback + // For now, we have to do it ourselves. + qDebug() << "Note: using plain QTimer for retry"; + _connectTimer->start(timeToNextRetry * 1000); + } +} + +void BluetoothWatch::unscheduleConnect() +{ + _connectAlignedTimer->stop(); + _connectTimer->stop(); +} + +void BluetoothWatch::connectToWatch() +{ + delete _socket; //Delete socket from previous connect if any. + _socket = new QBluetoothSocket(QBluetoothSocket::RfcommSocket); + + connect(_socket, SIGNAL(connected()), SLOT(handleSocketConnected())); + connect(_socket, SIGNAL(disconnected()), SLOT(handleSocketDisconnected())); + connect(_socket, SIGNAL(readyRead()), SLOT(handleSocketData())); + connect(_socket, SIGNAL(error(QBluetoothSocket::SocketError)), + SLOT(handleSocketError(QBluetoothSocket::SocketError))); + connect(_socket, SIGNAL(stateChanged(QBluetoothSocket::SocketState)), + SLOT(handleSocketState(QBluetoothSocket::SocketState))); + + _socket->connectToService(_address, 1, QIODevice::ReadWrite | QIODevice::Unbuffered); +} + +void BluetoothWatch::handleConnectTimer() +{ + connectToWatch(); +} + +void BluetoothWatch::handleLocalDevModeChanged(QBluetoothLocalDevice::HostMode state) +{ + qDebug() << "Local bluetooth device mode changed to" << state; + if (state == QBluetoothLocalDevice::HostPoweredOff) { + // Host bluetooth was powered down + // Assume the socket has been disconnected + handleSocketDisconnected(); + // Cancel any pending connection attempts + unscheduleConnect(); + } else { + // Host bluetooth might have been powered up + if (!_connected) { + scheduleConnect(); + } + } +} + +void BluetoothWatch::handleSocketConnected() +{ + if (!_connected) { + qDebug() << "connected"; + + _connected = true; + _connectRetries = 0; + + setupBluetoothWatch(); + + emit connected(); + } +} + +void BluetoothWatch::handleSocketDisconnected() +{ + // Signal disconnection if necessary + if (_connected) { + qDebug() << "disconnected"; + + _connected = false; + desetupBluetoothWatch(); + + emit disconnected(); + } + + // Setup reconnection attempt if necessary + if (_localDev->hostMode() != QBluetoothLocalDevice::HostPoweredOff) { + scheduleRetryConnect(); + } +} + +void BluetoothWatch::handleSocketError(QBluetoothSocket::SocketError error) +{ + qWarning() << "Socket error:" << error; + // Seems that sometimes a disconnection event may not be generated. + handleSocketDisconnected(); +} + +void BluetoothWatch::handleSocketState(QBluetoothSocket::SocketState state) +{ + qDebug() << "socket is in" << state; +} diff --git a/libsowatchbt/bluetoothwatch.h b/libsowatchbt/bluetoothwatch.h new file mode 100644 index 0000000..882e7c0 --- /dev/null +++ b/libsowatchbt/bluetoothwatch.h @@ -0,0 +1,74 @@ +#ifndef SOWATCHBT_BLUETOOTHWATCH_H +#define SOWATCHBT_BLUETOOTHWATCH_H + +#include +#include +#include +#include +#include +#include "sowatchbt_global.h" + +namespace sowatch +{ + +using QTM_PREPEND_NAMESPACE(QBluetoothSocket); +using QTM_PREPEND_NAMESPACE(QBluetoothAddress); +using QTM_PREPEND_NAMESPACE(QSystemAlignedTimer); +using QTM_PREPEND_NAMESPACE(QBluetoothLocalDevice); + +class SOWATCHBT_EXPORT BluetoothWatch : public Watch +{ + Q_OBJECT + +public: + BluetoothWatch(const QBluetoothAddress& address, QObject *parent = 0); + ~BluetoothWatch(); + + bool isConnected() const; + +protected: + /** Start the initial connection attempt, reset failed connection timers. */ + void scheduleConnect(); + /** Schedule an new connection attempt, consider the current one failed. */ + void scheduleRetryConnect(); + /** Cancel any pending connection attempts. */ + void unscheduleConnect(); + + /** Attempt a connection to the watch right now. */ + virtual void connectToWatch(); + + /** To be overriden; should configure a newly connected watch. */ + virtual void setupBluetoothWatch() = 0; + /** To be overriden; handle a sudden watch disconnection. */ + virtual void desetupBluetoothWatch() = 0; + +private slots: + void handleConnectTimer(); + void handleLocalDevModeChanged(QBluetoothLocalDevice::HostMode state); + void handleSocketConnected(); + void handleSocketDisconnected(); + void handleSocketError(QBluetoothSocket::SocketError error); + void handleSocketState(QBluetoothSocket::SocketState error); + +protected: + /** Local BT device used. */ + QBluetoothLocalDevice *_localDev; + /** BT address of the watch we are trying to connect to. */ + QBluetoothAddress _address; + /** Socket to the watch. */ + QBluetoothSocket *_socket; + /** Whether we have succesfully connected to the watch or not. */ + bool _connected; + +private: + // Timers to retry the connection when the watch is not found. + static const int connectRetryTimesSize = 6; + static const int connectRetryTimes[connectRetryTimesSize]; + short _connectRetries; + QTimer *_connectTimer; + QSystemAlignedTimer *_connectAlignedTimer; +}; + +} + +#endif // SOWATCHBT_BLUETOOTHWATCH_H diff --git a/libsowatchbt/bluetoothwatchscanner.cpp b/libsowatchbt/bluetoothwatchscanner.cpp new file mode 100644 index 0000000..eeae5f7 --- /dev/null +++ b/libsowatchbt/bluetoothwatchscanner.cpp @@ -0,0 +1,31 @@ +#include +#include + +#include "bluetoothwatchscanner.h" + +QTM_USE_NAMESPACE +using namespace sowatch; + +BluetoothWatchScanner::BluetoothWatchScanner(QObject *parent) : + WatchScanner(parent), + _agent(new QBluetoothServiceDiscoveryAgent(this)) +{ + connect(_agent, SIGNAL(finished()), this, SIGNAL(finished())); + connect(_agent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)), + this, SLOT(handleDiscoveredService(QBluetoothServiceInfo))); +} + +void BluetoothWatchScanner::start() +{ + if (_agent->isActive()) { + _agent->stop(); + } + _agent->start(); + qDebug() << "started bluetooth scan"; + emit started(); +} + +void BluetoothWatchScanner::setUuidFilter(const QBluetoothUuid & uuid) +{ + _agent->setUuidFilter(uuid); +} diff --git a/libsowatchbt/bluetoothwatchscanner.h b/libsowatchbt/bluetoothwatchscanner.h new file mode 100644 index 0000000..1ab974a --- /dev/null +++ b/libsowatchbt/bluetoothwatchscanner.h @@ -0,0 +1,37 @@ +#ifndef BLUETOOTHWATCHSCANNER_H +#define BLUETOOTHWATCHSCANNER_H + +#include +#include + +#include "sowatchbt_global.h" + +namespace sowatch +{ + +using QTM_PREPEND_NAMESPACE(QBluetoothServiceDiscoveryAgent); +using QTM_PREPEND_NAMESPACE(QBluetoothServiceInfo); +using QTM_PREPEND_NAMESPACE(QBluetoothUuid); + +class SOWATCHBT_EXPORT BluetoothWatchScanner : public WatchScanner +{ + Q_OBJECT + +public: + BluetoothWatchScanner(QObject *parent); + + void start(); + +protected: + void setUuidFilter(const QBluetoothUuid & uuid); + +protected slots: + virtual void handleDiscoveredService(const QBluetoothServiceInfo& info) = 0; + +private: + QBluetoothServiceDiscoveryAgent *_agent; +}; + +} + +#endif // BLUETOOTHWATCHSCANNER_H diff --git a/libsowatchbt/libsowatchbt.pro b/libsowatchbt/libsowatchbt.pro new file mode 100644 index 0000000..a707778 --- /dev/null +++ b/libsowatchbt/libsowatchbt.pro @@ -0,0 +1,38 @@ +TARGET = sowatchbt +TEMPLATE = lib +VERSION = 1.0.0 + +# Qt Mobility 1.2 +maemo5 { + CONFIG += mobility12 +} else { + CONFIG += mobility +} +MOBILITY += connectivity systeminfo + +DEFINES += SOWATCHBT_LIBRARY + +SOURCES += \ + bluetoothwatch.cpp \ + bluetoothwatchscanner.cpp + +HEADERS += sowatchbt.h sowatchbt_global.h \ + bluetoothwatch.h \ + bluetoothwatchscanner.h + +LIBS += -L$$OUT_PWD/../libsowatch/ -lsowatch +INCLUDEPATH += $$PWD/../libsowatch +DEPENDPATH += $$PWD/../libsowatch + +install_headers.files = $$HEADERS + +install_headers.path = /usr/include/sowatch +!isEmpty(MEEGO_VERSION_MAJOR)|maemo5 { + target.path = /opt/sowatch/lib + install_translations.path = /opt/sowatch/i18n +} else { + target.path = /usr/lib + install_translations.path = /usr/share/sowatch/i18n +} + +INSTALLS += install_headers target diff --git a/libsowatchbt/sowatchbt.h b/libsowatchbt/sowatchbt.h new file mode 100644 index 0000000..a60f835 --- /dev/null +++ b/libsowatchbt/sowatchbt.h @@ -0,0 +1,9 @@ +#ifndef SOWATCHBT_H +#define SOWATCHBT_H + +#include "sowatchbt_global.h" + +#include "bluetoothwatch.h" +#include "bluetoothwatchscanner.h" + +#endif // SOWATCHBT_H diff --git a/libsowatchbt/sowatchbt_global.h b/libsowatchbt/sowatchbt_global.h new file mode 100644 index 0000000..72c724e --- /dev/null +++ b/libsowatchbt/sowatchbt_global.h @@ -0,0 +1,12 @@ +#ifndef SOWATCHBT_GLOBAL_H +#define SOWATCHBT_GLOBAL_H + +#include + +#if defined(SOWATCHBT_LIBRARY) +# define SOWATCHBT_EXPORT Q_DECL_EXPORT +#else +# define SOWATCHBT_EXPORT Q_DECL_IMPORT +#endif + +#endif // SOWATCHBT_GLOBAL_H -- cgit v1.2.3