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