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