Fixed a bug that caused connections to not being found when keyboard is closed during...
[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         // Do not allow forced disconnection if connection was not started
196         // by this class.
197         if(!connected_)
198         {
199             return false;
200         }
201
202         QDBusMessage msg = QDBusMessage::createSignal("/com/nokia/icd_ui",
203                                                       "com.nokia.icd_ui",
204                                                       "disconnect");
205
206         QList<QVariant> arguments;
207         bool val = true;
208         arguments.append(QVariant(val));
209         msg.setArguments(arguments);
210
211         bool ret = QDBusConnection::systemBus().send(msg);
212
213         if(ret)
214         {
215             connected_ = false;
216         }
217
218         return ret;
219     }
220
221     connectionReady_ = false;
222     unsigned int flags;
223
224     flags = static_cast<unsigned int>(ICD_CONNECTION_FLAG_USER_EVENT);
225
226     icd2interface_->call(ICD_DBUS_API_DISCONNECT_REQ, QVariant(flags));
227     connected_ = false;
228     return true;
229 }
230
231 bool ConnectionManager::isConnected()
232 {
233     stateReady_ = false;
234     QDBusMessage rep = icd2interface_->call(ICD_DBUS_API_STATE_REQ);
235
236     unsigned int numOfReplies = rep.arguments().value(0).value<unsigned int>();
237
238     if(numOfReplies == 0)
239     {
240         return false;
241     }
242
243     waitSignal(&stateReady_);
244     return connected_;
245 }
246
247 bool ConnectionManager::scanConnections(QList<ConnectionManager::Connection>& connections,
248                                         ConnectionManager::ConnectionType type)
249 {
250     unsigned int flags = static_cast<unsigned int>(ICD_SCAN_REQUEST_ACTIVE_SAVED);
251     scanReady_ = false;
252     scannedConnections_ = 0;
253     connections_ = &connections;
254     searchType_ = type;
255
256     QDBusMessage rep = icd2interface_->call(ICD_DBUS_API_SCAN_REQ,
257                                             QVariant(flags));
258
259     numberOfConnections_ = rep.arguments().value(0).toList().size();
260
261     // For some reason, during call icd2 doesn't return any connections
262     // it is going to scan. However, it still scans them so use default value
263     // of 2.
264     if(numberOfConnections_ == 0)
265     {
266         numberOfConnections_ = 2;
267     }
268
269     bool ret = waitSignal(&scanReady_);
270     connections_ = 0;
271     return ret;
272 }
273
274 ConnectionManager::Error ConnectionManager::error() const
275 {
276     return error_;
277 }
278
279 void ConnectionManager::stateChange(const QDBusMessage& rep)
280 {
281     unsigned int status = rep.arguments().value(7).value<unsigned int>();
282
283     switch(status)
284     {
285     case ICD_STATE_CONNECTING:
286         break;
287     case ICD_STATE_CONNECTED:
288         connected_ = true;
289         stateReady_ = true;
290         break;
291     case ICD_STATE_DISCONNECTING:
292         break;
293     case ICD_STATE_DISCONNECTED:
294         connected_ = false;
295         stateReady_ = true;
296         break;
297     case ICD_STATE_LIMITED_CONN_ENABLED:
298         connected_ = true;
299         stateReady_ = true;
300         break;
301     case ICD_STATE_LIMITED_CONN_DISABLED:
302         connected_ = false;
303         stateReady_ = true;
304         break;
305     case ICD_STATE_SEARCH_START:
306         break;
307     case ICD_STATE_SEARCH_STOP:
308         break;
309     case ICD_STATE_INTERNAL_ADDRESS_ACQUIRED:
310         break;
311     default:
312         qDebug() << "Unknown connection status";
313         break;
314     }
315
316 }
317
318 void ConnectionManager::connectionChange(const QDBusMessage& rep)
319 {
320     unsigned int status = rep.arguments().value(6).value<unsigned int>();
321
322     switch(status)
323     {
324     case ICD_CONNECTION_SUCCESSFUL:
325         connected_ = true;
326         connectionReady_ = true;
327         break;
328     case ICD_CONNECTION_NOT_CONNECTED:
329         connected_ = false;
330         connectionReady_ = true;
331         break;
332     case ICD_CONNECTION_DISCONNECTED:
333         connected_ = false;
334         connectionReady_ = true;
335         break;
336     default:
337         qDebug() << "Unknown connection status";
338         break;
339     }
340
341 }
342
343 void ConnectionManager::scanResult(const QDBusMessage& rep)
344 {
345     if(!connections_)
346     {
347         return;
348     }
349
350     QList<QVariant> args = rep.arguments();
351
352     unsigned int status = args.value(0).value<unsigned int>();
353
354     if(status == ICD_SCAN_COMPLETE)
355     {
356         scannedConnections_++;
357     }
358
359     if(scannedConnections_ >= numberOfConnections_)
360     {
361         scanReady_ = true;
362         connections_ = 0;
363         return;
364     }
365
366     if(status != ICD_SCAN_NEW && status != ICD_SCAN_NOTIFY)
367     {
368         return;
369     }
370
371     Connection connection;
372
373     QString type = args.value(7).toString();
374
375     if(type == "GPRS")
376     {
377         if(searchType_ == WLAN)
378         {
379             return;
380         }
381
382         connection.type = GPRS;
383     }
384     else if(type == "WLAN_INFRA" || type == "WLAN_ADHOC")
385     {
386         if(searchType_ == GPRS)
387         {
388             return;
389         }
390
391         connection.type = WLAN;
392     }
393     else
394     {
395         qDebug() << "Unknown connection type: " << type;
396         return;
397     }
398
399     connection.id = QString(args.value(10).toByteArray());
400     connection.name = args.value(8).toString();
401     connection.strength = args.value(11).toInt();
402
403     for(int i = 0; i < connections_->size(); i++)
404     {
405         if(connections_->at(i).id == connection.id)
406         {
407             if(status == ICD_SCAN_NEW)
408             {
409                 connections_->insert(i, connection);
410             }
411
412             return;
413         }
414     }
415
416     connections_->push_back(connection);
417 }
418
419 bool ConnectionManager::waitSignal(bool* ready)
420 {
421     timeout_ = false;
422     timer_ = startTimer(TIMEOUT);
423
424     while(!*ready && !timeout_)
425     {
426         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
427     }
428
429     killTimer(timer_);
430
431     return *ready || !timeout_;
432 }
433
434 void ConnectionManager::timerEvent(QTimerEvent* event)
435 {
436     Q_UNUSED(event);
437     killTimer(timer_);
438     timeout_ = true;
439     timer_ = 0;
440
441     qDebug() << "Connection request timed out";
442 }