Changed daemon to auto disconnect if network connection was initialized by itself.
[jenirok] / src / common / connectionmanager.cpp
1 /*
2  * This file is part of Jenirok.
3  *
4  * Jenirok is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Jenirok is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Jenirok.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  */
18
19 #include <QDebug>
20 #include <QApplication>
21 #include <QtCore/QTimerEvent>
22 #include <QtCore/QVariant>
23 #include <QtDBus/QDBusArgument>
24 #include <icd/dbus_api.h>
25 #include "connectionmanager.h"
26
27
28 ConnectionManager::ConnectionManager(QObject* parent): QObject(parent),
29 blocking_(true), stateReady_(false), connectionReady_(false), scanReady_(false),
30 connected_(false), timeout_(false), numberOfConnections_(0),
31 scannedConnections_(0), timer_(0), connections_(false)
32 {
33     QDBusConnection systemBus = QDBusConnection::systemBus();
34
35     icd2interface_ = new QDBusInterface(ICD_DBUS_API_INTERFACE,
36                                         ICD_DBUS_API_PATH, ICD_DBUS_API_INTERFACE,
37                                         systemBus, this);
38
39     systemBus.connect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
40                       ICD_DBUS_API_INTERFACE, ICD_DBUS_API_STATE_SIG,
41                       this, SLOT(stateChange(const QDBusMessage&)));
42
43     systemBus.connect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
44                       ICD_DBUS_API_INTERFACE, ICD_DBUS_API_CONNECT_SIG,
45                       this, SLOT(connectionChange(const QDBusMessage&)));
46
47     systemBus.connect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
48                       ICD_DBUS_API_INTERFACE, ICD_DBUS_API_SCAN_SIG,
49                       this, SLOT(scanResult(const QDBusMessage&)));
50
51 }
52
53 ConnectionManager::~ConnectionManager()
54 {
55     QDBusConnection systemBus = QDBusConnection::systemBus();
56
57     systemBus.disconnect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
58                          ICD_DBUS_API_INTERFACE, ICD_DBUS_API_STATE_SIG,
59                          this, SLOT(stateChange(const QDBusMessage&)));
60
61     systemBus.disconnect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
62                          ICD_DBUS_API_INTERFACE, ICD_DBUS_API_CONNECT_SIG,
63                          this, SLOT(connectionChange(const QDBusMessage&)));
64
65     systemBus.disconnect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
66                          ICD_DBUS_API_INTERFACE, ICD_DBUS_API_SCAN_SIG,
67                          this, SLOT(scanResult(const QDBusMessage&)));
68 }
69
70 void ConnectionManager::setBlocking(bool value)
71 {
72     blocking_ = value;
73 }
74
75 bool ConnectionManager::connect()
76 {
77     connectionReady_ = false;
78     unsigned int flags = static_cast<unsigned int>(ICD_CONNECTION_FLAG_USER_EVENT);
79     icd2interface_->call(ICD_DBUS_API_CONNECT_REQ, QVariant(flags));
80
81     if(blocking_)
82     {
83         waitSignal(&connectionReady_);
84         return connected_;
85     }
86
87     return true;
88 }
89
90 bool ConnectionManager::connect(ConnectionManager::Connection const& connection)
91 {
92     connectionReady_ = false;
93     QDBusArgument arg;
94     arg.beginStructure();
95     arg << connection.serviceType
96         << connection.serviceAttributes
97         << connection.serviceID
98         << connection.networkType
99         << connection.networkAttributes
100         << connection.networkID;
101     arg.endStructure();
102
103     unsigned int flags = static_cast<unsigned int>(ICD_CONNECTION_FLAG_USER_EVENT);
104     QDBusMessage rep = icd2interface_->call(ICD_DBUS_API_CONNECT_REQ,
105                                             flags, arg.asVariant());
106
107     qDebug() << rep.errorName() << rep.errorMessage();
108
109     if(blocking_)
110     {
111         waitSignal(&connectionReady_);
112         return connected_;
113     }
114
115     return true;
116 }
117
118 bool ConnectionManager::disconnect(bool force)
119 {
120     // Forced disconnect is not allowed if connection
121     // was not initialized by this class
122     if(!connected_ && force)
123     {
124         return false;
125     }
126
127     connectionReady_ = false;
128     unsigned int flags;
129
130     if(force)
131     {
132         flags = static_cast<unsigned int>(ICD_CONNECTION_FLAG_UI_EVENT);
133     }
134     else
135     {
136         flags = static_cast<unsigned int>(ICD_CONNECTION_FLAG_USER_EVENT);
137     }
138
139     icd2interface_->call(ICD_DBUS_API_DISCONNECT_REQ, QVariant(flags));
140     connected_ = false;
141     return true;
142 }
143
144 bool ConnectionManager::isConnected()
145 {
146     stateReady_ = false;
147     QDBusMessage rep = icd2interface_->call(ICD_DBUS_API_STATE_REQ);
148
149     unsigned int numOfReplies = rep.arguments().value(0).value<unsigned int>();
150
151     if(numOfReplies == 0)
152     {
153         emit isConnectedReply(false);
154         return false;
155     }
156
157     if(blocking_)
158     {
159         waitSignal(&stateReady_);
160         return connected_;
161     }
162
163     return true;
164 }
165
166 bool ConnectionManager::scanConnections(QList<ConnectionManager::Connection>& connections)
167 {
168     unsigned int flags = static_cast<unsigned int>(ICD_SCAN_REQUEST_ACTIVE);
169     scanReady_ = false;
170     scannedConnections_ = 0;
171     connections_ = &connections;
172     QDBusMessage rep = icd2interface_->call(ICD_DBUS_API_SCAN_REQ, QVariant(flags));
173
174     numberOfConnections_ = rep.arguments().value(0).toList().size();
175
176     if(numberOfConnections_ == 0)
177     {
178         connections_ = 0;
179         return false;
180     }
181
182     if(blocking_)
183     {
184         bool ret = waitSignal(&scanReady_);
185         connections_ = 0;
186         return ret;
187     }
188
189     return true;
190 }
191
192 void ConnectionManager::stateChange(const QDBusMessage& rep)
193 {
194     unsigned int status = rep.arguments().value(7).value<unsigned int>();
195
196     switch(status)
197     {
198     case ICD_STATE_CONNECTING:
199         break;
200     case ICD_STATE_CONNECTED:
201         connected_ = true;
202         stateReady_ = true;
203         break;
204     case ICD_STATE_DISCONNECTING:
205         break;
206     case ICD_STATE_DISCONNECTED:
207         connected_ = false;
208         stateReady_ = true;
209         break;
210     case ICD_STATE_LIMITED_CONN_ENABLED:
211         connected_ = true;
212         stateReady_ = true;
213         break;
214     case ICD_STATE_LIMITED_CONN_DISABLED:
215         connected_ = false;
216         stateReady_ = true;
217         break;
218     case ICD_STATE_SEARCH_START:
219         break;
220     case ICD_STATE_SEARCH_STOP:
221         break;
222     case ICD_STATE_INTERNAL_ADDRESS_ACQUIRED:
223         break;
224     default:
225         qDebug() << "Unknown connection status";
226         break;
227     }
228
229     if(stateReady_)
230     {
231         emit isConnectedReply(connected_);
232     }
233
234 }
235
236 void ConnectionManager::connectionChange(const QDBusMessage& rep)
237 {
238     unsigned int status = rep.arguments().value(6).value<unsigned int>();
239
240     switch(status)
241     {
242     case ICD_CONNECTION_SUCCESSFUL:
243         connected_ = true;
244         connectionReady_ = true;
245         break;
246     case ICD_CONNECTION_NOT_CONNECTED:
247         connected_ = false;
248         connectionReady_ = true;
249         break;
250     case ICD_CONNECTION_DISCONNECTED:
251         connected_ = false;
252         connectionReady_ = true;
253         break;
254     default:
255         qDebug() << "Unknown connection status";
256         break;
257     }
258
259     if(connectionReady_)
260     {
261         emit connectReply(connected_);
262     }
263 }
264
265 void ConnectionManager::scanResult(const QDBusMessage& rep)
266 {
267     if(!connections_)
268     {
269         return;
270     }
271
272     QList<QVariant> args = rep.arguments();
273
274     unsigned int status = args.value(0).value<unsigned int>();
275
276     if(status == ICD_SCAN_COMPLETE)
277     {
278         scannedConnections_++;
279     }
280
281     if(scannedConnections_ >= numberOfConnections_)
282     {
283         scanReady_ = true;
284         emit scanReady();
285         return;
286     }
287
288     if(status != ICD_SCAN_NEW)
289     {
290         return;
291     }
292
293     Connection connection;
294     connection.serviceType = args.value(2).toString();
295     connection.serviceAttributes = args.value(4).value<unsigned int>();
296     connection.serviceID = args.value(5).toString();
297     connection.networkName = args.value(8).toString();
298     connection.networkType = args.value(7).toString();
299     connection.networkAttributes = args.value(9).value<unsigned int>();
300     connection.networkID = args.value(10).toByteArray();
301
302     emit newConnection(connection);
303
304     connections_->push_back(connection);
305 }
306
307 bool ConnectionManager::waitSignal(bool* ready)
308 {
309     timeout_ = false;
310     timer_ = startTimer(TIMEOUT);
311
312     while(!*ready && !timeout_)
313     {
314         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
315     }
316
317     killTimer(timer_);
318
319     return *ready || !timeout_;
320 }
321
322 void ConnectionManager::timerEvent(QTimerEvent* event)
323 {
324     Q_UNUSED(event);
325     killTimer(timer_);
326     timeout_ = true;
327     timer_ = 0;
328
329     qDebug() << "Connection request timed out";
330 }