Changelog updated.
[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 <QtCore/QDebug>
20 #include <QtCore/QTimerEvent>
21 #include <QtCore/QVariant>
22 #include <QtCore/QStringList>
23 #include <QtGui/QApplication>
24 #include <QtDBus/QDBusArgument>
25 #include <QtDBus/QDBusConnection>
26 #include <icd/dbus_api.h>
27 #include "connectionmanager.h"
28
29 bool ConnectionManager::connected_ = false;
30
31 ConnectionManager::ConnectionManager(QObject* parent): QObject(parent),
32 stateReady_(false), connectionReady_(false), scanReady_(false),
33 timeout_(false), numberOfConnections_(0),
34 scannedConnections_(0), timer_(0), searchType_(NO_TYPE), error_(NO_ERROR), connections_(0)
35 {
36     QDBusConnection systemBus = QDBusConnection::systemBus();
37
38     icd2interface_ = new QDBusInterface(ICD_DBUS_API_INTERFACE,
39                                         ICD_DBUS_API_PATH, ICD_DBUS_API_INTERFACE,
40                                         systemBus, this);
41
42     systemBus.connect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
43                       ICD_DBUS_API_INTERFACE, ICD_DBUS_API_STATE_SIG,
44                       this, SLOT(stateChange(const QDBusMessage&)));
45
46     systemBus.connect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
47                       ICD_DBUS_API_INTERFACE, ICD_DBUS_API_CONNECT_SIG,
48                       this, SLOT(connectionChange(const QDBusMessage&)));
49
50     systemBus.connect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
51                       ICD_DBUS_API_INTERFACE, ICD_DBUS_API_SCAN_SIG,
52                       this, SLOT(scanResult(const QDBusMessage&)));
53
54 }
55
56 ConnectionManager::~ConnectionManager()
57 {
58     QDBusConnection systemBus = QDBusConnection::systemBus();
59
60     systemBus.disconnect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
61                          ICD_DBUS_API_INTERFACE, ICD_DBUS_API_STATE_SIG,
62                          this, SLOT(stateChange(const QDBusMessage&)));
63
64     systemBus.disconnect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
65                          ICD_DBUS_API_INTERFACE, ICD_DBUS_API_CONNECT_SIG,
66                          this, SLOT(connectionChange(const QDBusMessage&)));
67
68     systemBus.disconnect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
69                          ICD_DBUS_API_INTERFACE, ICD_DBUS_API_SCAN_SIG,
70                          this, SLOT(scanResult(const QDBusMessage&)));
71 }
72
73 bool ConnectionManager::connect()
74 {
75     connectionReady_ = false;
76     unsigned int flags = static_cast<unsigned int>(ICD_CONNECTION_FLAG_USER_EVENT);
77     icd2interface_->call(ICD_DBUS_API_CONNECT_REQ, QVariant(flags));
78
79     waitSignal(&connectionReady_);
80     return connected_;
81 }
82
83 bool ConnectionManager::connect(ConnectionManager::Connection const& connection)
84 {
85     return connect(connection.id);
86 }
87
88 bool ConnectionManager::connect(QString const& id)
89 {
90     QDBusMessage msg = QDBusMessage::createMethodCall("com.nokia.icd",
91                                                       "/com/nokia/icd",
92                                                       "com.nokia.icd",
93                                                       "connect");
94     QList<QVariant> arguments;
95
96     arguments.append(QVariant(id));
97
98     unsigned int val = 0;
99
100     arguments.append(QVariant(val));
101
102     msg.setArguments(arguments);
103
104     QDBusMessage rep = QDBusConnection::systemBus().call(msg);
105
106     if(rep.type() == QDBusMessage::ErrorMessage)
107     {
108         if(rep.errorName() == "com.nokia.icd.error.invalid_iap")
109         {
110             error_ = INVALID_IAP;
111         }
112         else
113         {
114             error_ = UNKNOWN_ERROR;
115         }
116
117         connected_ = false;
118
119         return false;
120     }
121
122     connected_ = true;
123
124     return true;
125 }
126
127 bool ConnectionManager::getBestConnection(Connection& connection, ConnectionType type)
128 {
129     QList<Connection> connections;
130
131     if(!scanConnections(connections))
132     {
133         qDebug() << "Unable to scan connections";
134         return false;
135     }
136
137     if(connections.size() == 0)
138     {
139         error_ = NO_AVAILABLE_CONNECTIONS;
140         return false;
141     }
142
143     int biggestWlan = -1;
144     int biggestGprs = -1;
145     int bestWlan = -1;
146     int bestGprs = -1;
147
148     for(int i = 0; i < connections.size(); i++)
149     {
150         switch(connections.at(i).type)
151         {
152         case WLAN:
153             if(type != GPRS && connections.at(i).strength > biggestWlan)
154             {
155                 biggestWlan = connections.at(i).strength;
156                 bestWlan = i;
157             }
158             break;
159
160         case GPRS:
161             if(type != WLAN && connections.at(i).strength > biggestGprs)
162             {
163                 biggestGprs = connections.at(i).strength;
164                 bestGprs = i;
165             }
166             break;
167
168         default:
169             qDebug() << "Unknown connection type";
170         }
171     }
172
173     if(bestWlan >= 0)
174     {
175         connection = connections.at(bestWlan);
176         return true;
177     }
178     else if(bestGprs >= 0)
179     {
180         connection = connections.at(bestGprs);
181         return true;
182     }
183     else
184     {
185         error_ = NO_AVAILABLE_CONNECTIONS;
186         return false;
187     }
188
189 }
190
191 bool ConnectionManager::disconnect(bool force)
192 {
193     if(force)
194     {
195         QDBusMessage msg = QDBusMessage::createSignal("/com/nokia/icd_ui",
196                                                       "com.nokia.icd_ui",
197                                                       "disconnect");
198
199         QList<QVariant> arguments;
200         bool val = true;
201         arguments.append(QVariant(val));
202         msg.setArguments(arguments);
203
204         bool ret = QDBusConnection::systemBus().send(msg);
205
206         if(ret)
207         {
208             connected_ = false;
209         }
210
211         return ret;
212     }
213
214     connectionReady_ = false;
215     unsigned int flags;
216
217     flags = static_cast<unsigned int>(ICD_CONNECTION_FLAG_USER_EVENT);
218
219     icd2interface_->call(ICD_DBUS_API_DISCONNECT_REQ, QVariant(flags));
220     connected_ = false;
221     return true;
222 }
223
224 bool ConnectionManager::isConnected()
225 {
226     stateReady_ = false;
227     QDBusMessage rep = icd2interface_->call(ICD_DBUS_API_STATE_REQ);
228
229     unsigned int numOfReplies = rep.arguments().value(0).value<unsigned int>();
230
231     if(numOfReplies == 0)
232     {
233         return false;
234     }
235
236     waitSignal(&stateReady_);
237     return connected_;
238 }
239
240 bool ConnectionManager::scanConnections(QList<ConnectionManager::Connection>& connections,
241                                         ConnectionManager::ConnectionType type)
242 {
243     unsigned int flags = static_cast<unsigned int>(ICD_SCAN_REQUEST_ACTIVE_SAVED);
244     scanReady_ = false;
245     scannedConnections_ = 0;
246     connections_ = &connections;
247     searchType_ = type;
248
249     QDBusMessage rep = icd2interface_->call(ICD_DBUS_API_SCAN_REQ,
250                                             QVariant(flags));
251
252     numberOfConnections_ = rep.arguments().value(0).toList().size();
253
254     // For some reason, during call icd2 doesn't return any connections
255     // it is going to scan. However, it still scans them so use default value
256     // of 2.
257     if(numberOfConnections_ == 0)
258     {
259         numberOfConnections_ = 2;
260     }
261
262     bool ret = waitSignal(&scanReady_);
263     connections_ = 0;
264     return ret;
265 }
266
267 ConnectionManager::Error ConnectionManager::error() const
268 {
269     return error_;
270 }
271
272 void ConnectionManager::stateChange(const QDBusMessage& rep)
273 {
274     unsigned int status = rep.arguments().value(7).value<unsigned int>();
275
276     switch(status)
277     {
278     case ICD_STATE_CONNECTING:
279         break;
280     case ICD_STATE_CONNECTED:
281         connected_ = true;
282         stateReady_ = true;
283         break;
284     case ICD_STATE_DISCONNECTING:
285         break;
286     case ICD_STATE_DISCONNECTED:
287         connected_ = false;
288         stateReady_ = true;
289         break;
290     case ICD_STATE_LIMITED_CONN_ENABLED:
291         connected_ = true;
292         stateReady_ = true;
293         break;
294     case ICD_STATE_LIMITED_CONN_DISABLED:
295         connected_ = false;
296         stateReady_ = true;
297         break;
298     case ICD_STATE_SEARCH_START:
299         break;
300     case ICD_STATE_SEARCH_STOP:
301         break;
302     case ICD_STATE_INTERNAL_ADDRESS_ACQUIRED:
303         break;
304     default:
305         qDebug() << "Unknown connection status";
306         break;
307     }
308
309 }
310
311 void ConnectionManager::connectionChange(const QDBusMessage& rep)
312 {
313     unsigned int status = rep.arguments().value(6).value<unsigned int>();
314
315     switch(status)
316     {
317     case ICD_CONNECTION_SUCCESSFUL:
318         connected_ = true;
319         connectionReady_ = true;
320         break;
321     case ICD_CONNECTION_NOT_CONNECTED:
322         connected_ = false;
323         connectionReady_ = true;
324         break;
325     case ICD_CONNECTION_DISCONNECTED:
326         connected_ = false;
327         connectionReady_ = true;
328         break;
329     default:
330         qDebug() << "Unknown connection status";
331         break;
332     }
333
334 }
335
336 void ConnectionManager::scanResult(const QDBusMessage& rep)
337 {
338     if(!connections_)
339     {
340         return;
341     }
342
343     QList<QVariant> args = rep.arguments();
344
345     unsigned int status = args.value(0).value<unsigned int>();
346
347     if(status == ICD_SCAN_COMPLETE)
348     {
349         scannedConnections_++;
350     }
351
352     if(scannedConnections_ >= numberOfConnections_)
353     {
354         scanReady_ = true;
355         connections_ = 0;
356         return;
357     }
358
359     if(status != ICD_SCAN_NEW && status != ICD_SCAN_NOTIFY)
360     {
361         return;
362     }
363
364     Connection connection;
365
366     QString type = args.value(7).toString();
367
368     if(type == "GPRS")
369     {
370         if(searchType_ == WLAN)
371         {
372             return;
373         }
374
375         connection.type = GPRS;
376     }
377     else if(type == "WLAN_INFRA" || type == "WLAN_ADHOC")
378     {
379         if(searchType_ == GPRS)
380         {
381             return;
382         }
383
384         connection.type = WLAN;
385     }
386     else
387     {
388         qDebug() << "Unknown connection type: " << type;
389         return;
390     }
391
392     connection.id = QString(args.value(10).toByteArray());
393     connection.name = args.value(8).toString();
394     connection.strength = args.value(11).toInt();
395
396     for(int i = 0; i < connections_->size(); i++)
397     {
398         if(connections_->at(i).id == connection.id)
399         {
400             if(status == ICD_SCAN_NEW)
401             {
402                 connections_->replace(i, connection);
403             }
404
405             return;
406         }
407     }
408
409     connections_->push_back(connection);
410 }
411
412 bool ConnectionManager::waitSignal(bool* ready)
413 {
414     timeout_ = false;
415     timer_ = startTimer(TIMEOUT);
416
417     while(!*ready && !timeout_)
418     {
419         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
420     }
421
422     killTimer(timer_);
423
424     return *ready || !timeout_;
425 }
426
427 void ConnectionManager::timerEvent(QTimerEvent* event)
428 {
429     Q_UNUSED(event);
430     killTimer(timer_);
431     timeout_ = true;
432     timer_ = 0;
433
434     qDebug() << "Connection request timed out";
435 }