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 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 libsowatchbt/bluetoothwatch.cpp (limited to 'libsowatchbt/bluetoothwatch.cpp') 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; +} -- cgit v1.2.3