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