summaryrefslogtreecommitdiff
path: root/libsowatchbt/bluetoothwatch.cpp
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/bluetoothwatch.cpp
parent6003bf81107dd9be51589c074b74c5af82bfc8ab (diff)
downloadsowatch-ccd19d2b7ee4184503ea46b98333b27a5613190e.tar.gz
sowatch-ccd19d2b7ee4184503ea46b98333b27a5613190e.zip
big change: a new abstract BluetoothWatch class
Diffstat (limited to 'libsowatchbt/bluetoothwatch.cpp')
-rw-r--r--libsowatchbt/bluetoothwatch.cpp176
1 files changed, 176 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;
+}