summaryrefslogtreecommitdiff
path: root/libsowatchbt
diff options
context:
space:
mode:
authorJavier S. Pedro <maemo@javispedro.com>2013-05-08 18:04:31 +0200
committerJavier S. Pedro <maemo@javispedro.com>2013-05-08 18:04:31 +0200
commitccd19d2b7ee4184503ea46b98333b27a5613190e (patch)
treef78cc3f3c946d7a4f8167f72fb20703c65494d84 /libsowatchbt
parent6003bf81107dd9be51589c074b74c5af82bfc8ab (diff)
downloadsowatch-ccd19d2b7ee4184503ea46b98333b27a5613190e.tar.gz
sowatch-ccd19d2b7ee4184503ea46b98333b27a5613190e.zip
big change: a new abstract BluetoothWatch class
Diffstat (limited to 'libsowatchbt')
-rw-r--r--libsowatchbt/bluetoothwatch.cpp176
-rw-r--r--libsowatchbt/bluetoothwatch.h74
-rw-r--r--libsowatchbt/bluetoothwatchscanner.cpp31
-rw-r--r--libsowatchbt/bluetoothwatchscanner.h37
-rw-r--r--libsowatchbt/libsowatchbt.pro38
-rw-r--r--libsowatchbt/sowatchbt.h9
-rw-r--r--libsowatchbt/sowatchbt_global.h12
7 files changed, 377 insertions, 0 deletions
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 <QtConnectivity/QBluetoothAddress>
+#include <QtConnectivity/QBluetoothSocket>
+#include <QtConnectivity/QBluetoothLocalDevice>
+#include <QtSystemInfo/QSystemAlignedTimer>
+#include <sowatch.h>
+#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 <QtConnectivity/QBluetoothDeviceInfo>
+#include <QtConnectivity/QBluetoothAddress>
+
+#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 <sowatch.h>
+#include <QtConnectivity/QBluetoothServiceDiscoveryAgent>
+
+#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 <QtCore/qglobal.h>
+
+#if defined(SOWATCHBT_LIBRARY)
+# define SOWATCHBT_EXPORT Q_DECL_EXPORT
+#else
+# define SOWATCHBT_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // SOWATCHBT_GLOBAL_H