Fixed a bug that caused wlan connections to not being found in daemon.
[jenirok] / src / daemon / calllistener.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/QTimer>
21 #include "calllistener.h"
22 #include "settings.h"
23 #include "cache.h"
24 #include "contactmanager.h"
25 #include "connectionmanager.h"
26 #include "sourcecoreconfig.h"
27 #include "db.h"
28
29 namespace
30 {
31     const QString CALL_SERVICE_NAME = "com.nokia.csd";
32     const QString CALL_SERVICE_PATH = "/com/nokia/csd/call";
33     const QString CALL_SERVICE_INTERFACE = "com.nokia.csd.Call";
34     const QString CALL_SERVICE_INSTANCE_NAME = "com.nokia.csd.Call.Instance";
35     const QString CALL_SIGNAL_INCOMING = "Coming";
36     const QString CALL_SIGNAL_RELEASE = "Release";
37     const QString CALL_SIGNAL_TERMINATED = "Terminated";
38 }
39
40 QDBusConnection CallListener::systemBus_ = QDBusConnection::systemBus();
41
42 CallListener::CallListener(): source_(0),
43 closeConnection_(false), initialized_(false), box_(0), label_(0),
44 retries_(-1), timer_(0)
45 {
46 }
47
48 CallListener::~CallListener()
49 {
50     end();
51 }
52
53 bool CallListener::begin()
54 {
55     if(Settings::instance()->getConnectionType() == Settings::ALWAYS_ASK)
56     {
57         qDebug() << "Bad connection settings, unable to start";
58         return false;
59     }
60
61     sourceId_ = Source::stringToId(Settings::instance()->get("source"));
62     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
63     config->getConfig(sourceConfig_);
64     delete config;
65
66     systemBus_.connect(CALL_SERVICE_NAME,
67                        CALL_SERVICE_PATH,
68                        CALL_SERVICE_INTERFACE,
69                        CALL_SIGNAL_INCOMING,
70                        this,
71                        SLOT(incomingCall(QDBusObjectPath, QString)));
72
73     systemBus_.connect(CALL_SERVICE_NAME,
74                        CALL_SERVICE_PATH,
75                        CALL_SERVICE_INTERFACE,
76                        CALL_SIGNAL_RELEASE,
77                        this,
78                        SLOT(callTerminate()));
79
80     qDebug() << "Starting...";
81
82     return true;
83
84 }
85
86 void CallListener::end()
87 {
88     systemBus_.disconnect(CALL_SERVICE_NAME,
89                           CALL_SERVICE_PATH,
90                           CALL_SERVICE_INTERFACE,
91                           CALL_SIGNAL_INCOMING,
92                           this,
93                           SLOT(incomingCall(QDBusObjectPath, QString)));
94
95     systemBus_.disconnect(CALL_SERVICE_NAME,
96                           CALL_SERVICE_PATH,
97                           CALL_SERVICE_INTERFACE,
98                           CALL_SIGNAL_RELEASE,
99                           this,
100                           SLOT(callTerminate()));
101
102     searchClose();
103     sourceConfig_.clear();
104
105 }
106
107 void CallListener::search(Source::SearchDetails const& details)
108 {
109     qDebug() << "Search called";
110
111     searchInit();
112
113     Source::Result result;
114
115     if(Cache::instance().findItem(details.query, result))
116     {
117
118         qDebug() << "Found from cache";
119
120         showDelayedResult(createResult(result.name,
121                                        result.street,
122                                        result.city), BANNER_DELAY);
123     }
124     else
125     {
126         retries_ = 0;
127         currentSearch_ = details.query;
128
129         if(!handleConnection())
130         {
131             qDebug() << "Unable to connect";
132             return;
133         }
134
135         showDelayedResult(tr("Searching..."), BANNER_DELAY);
136
137         qDebug() << "Starting to search...";
138
139         source_->search(details);
140     }
141
142 }
143
144 void CallListener::requestFinished(QVector <Source::Result> const& results,
145                                    Source::SearchDetails const& details,
146                                    bool error)
147 {
148     // If box is not visible, the call must have been terminated already
149     if(!initialized_ || !box_->isVisible())
150     {
151         return;
152     }
153
154     QString message;
155
156     if(error)
157     {
158         qDebug() << "Error: " << source_->errorString();
159
160         if(retries_ < SEARCH_RETRIES && retries_ >= 0)
161         {
162             retries_++;
163             source_->search(Source::SearchDetails(currentSearch_));
164             return;
165         }
166         else
167         {
168             timedMessage_ = "";
169             QString errorString;
170             Source::Error error = source_->error();
171
172             switch(error)
173             {
174             case Source::TIMEOUT:
175                 errorString = tr("Request timed out");
176                 break;
177             default:
178                 errorString = source_->errorString();
179                 break;
180             }
181
182             showError(tr("Searching failed:") + " " + errorString + ".");
183         }
184     }
185     else
186     {
187         timedMessage_ = "";
188
189         if(results.size() == 0)
190         {
191             message = tr("Phone number was not found");
192             showResult(message);
193         }
194         else
195         {
196             message = createResult(results.at(0).name, results.at(0).street,
197                                    results.at(0).city);
198             showResult(message);
199             Source::Result result = results.at(0);
200             result.number = details.query;
201             Cache::instance().addItem(result);
202         }
203     }
204
205     retries_ = -1;
206     currentSearch_ = "";
207
208     if(closeConnection_)
209     {
210         closeConnection_ = false;
211         ConnectionManager cm;
212         cm.disconnect(true);
213     }
214 }
215
216 QString CallListener::createResult(QString const& name, QString const& street, QString const& city)
217 {
218     QString result = "<b>" + name + "</b>";
219
220     if(!street.isEmpty() || !city.isEmpty())
221     {
222         result += "<br>";
223
224         if(!street.isEmpty())
225         {
226             result += street + ", ";
227         }
228
229         result += city;
230     }
231
232     return result;
233 }
234
235 void CallListener::showResult(QString const& text)
236 {
237     if(!initialized_)
238     {
239         return;
240     }
241
242     label_->setText("<font color='black'>" + text + "</font>");
243
244     if(box_->isVisible())
245     {
246         box_->hide();
247     }
248
249     box_->show();
250 }
251
252 void CallListener::incomingCall(QDBusObjectPath path, QString number)
253 {
254     ContactManager cm;
255
256     if(!cm.numberExists(number))
257     {
258         qDebug() << "Number doesn't exist";
259
260         systemBus_.connect(CALL_SERVICE_NAME,
261                            path.path(),
262                            CALL_SERVICE_INSTANCE_NAME,
263                            CALL_SIGNAL_TERMINATED,
264                            this,
265                            SLOT(callTerminate()));
266
267         search(Source::SearchDetails(number));
268     }
269     else
270     {
271         qDebug() << "Number exists";
272     }
273 }
274
275 void CallListener::callTerminate()
276 {
277     if(box_ && box_->isVisible())
278     {
279         box_->hide();
280     }
281
282     if(closeConnection_)
283     {
284         closeConnection_ = false;
285         ConnectionManager cm;
286         cm.disconnect(true);
287     }
288
289     searchClose();
290 }
291
292 void CallListener::showDelayedResult(QString const& text, int delay)
293 {
294     timedMessage_ = text;
295     QTimer::singleShot(delay, this, SLOT(showTimedMessage()));
296 }
297
298 void CallListener::showTimedMessage()
299 {
300     if(timedMessage_.size() == 0 || !initialized_)
301     {
302         return;
303     }
304
305     showResult(timedMessage_);
306
307     timedMessage_ = "";
308 }
309
310 void CallListener::searchInit()
311 {
312     qDebug() << "Initializing search...";
313
314     if(initialized_)
315     {
316         qDebug() << "Already initialized";
317         return;
318     }
319
320     source_ = Source::getSource(sourceId_);
321     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
322     config->loadFromConfig(sourceConfig_);
323     config->apply(source_);
324     delete config;
325     source_->setMaxResults(1);
326     source_->setFindNumber(false);
327     source_->setTimeout(REQUEST_TIMEOUT);
328
329     connect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
330                                            Source::SearchDetails const&, bool)),
331                                            this, SLOT(requestFinished(QVector <Source::Result> const&,
332                                                                       Source::SearchDetails const&, bool)));
333     box_ = new InformationBox;
334     label_ = new QLabel("", box_);
335     label_->setMargin(8);
336     label_->setWordWrap(true);
337     box_->setWidget(label_);
338     initialized_ = true;
339 }
340
341 void CallListener::searchClose()
342 {
343     initialized_ = false;
344
345     qDebug() << "Closing search...";
346
347     if(source_)
348     {
349         disconnect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
350                                                   Source::SearchDetails const&, bool)),
351                                                   this, SLOT(requestFinished(QVector <Source::Result> const&,
352                                                                              Source::SearchDetails const&, bool)));
353     }
354
355     delete source_;
356     source_ = 0;
357     delete box_;
358     box_ = 0;
359     label_ = 0;
360 }
361
362 bool CallListener::handleConnection()
363 {
364     if(!initialized_)
365     {
366         return false;
367     }
368
369     ConnectionManager cm;
370
371     if(cm.isConnected())
372     {
373         closeConnection_ = false;
374         return true;
375     }
376
377     closeConnection_ = true;
378
379     Settings::ConnectionType configType = Settings::instance()->getConnectionType();
380
381     if(configType == Settings::ALWAYS_ASK)
382     {
383         showError(tr("Automatic connecting is not allowed by settings."), BANNER_DELAY);
384         return false;
385     }
386
387     showDelayedResult(tr("Connecting..."), BANNER_DELAY);
388
389     ConnectionManager::Connection best;
390
391     ConnectionManager::ConnectionType lookupType = ConnectionManager::NO_TYPE;
392
393     switch(configType)
394     {
395     case Settings::WLAN:
396         lookupType = ConnectionManager::WLAN;
397         break;
398     case Settings::GPRS:
399         lookupType = ConnectionManager::GPRS;
400         break;
401     default:
402         lookupType = ConnectionManager::NO_TYPE;
403         break;
404     }
405
406     int cretries = 0;
407     int scans = 0;
408     bool found = false;
409     int maxScans = GPRS_SCANS;
410
411     if(lookupType == ConnectionManager::WLAN)
412     {
413         maxScans = WLAN_SCANS;
414     }
415
416     while(cretries < CONNECTION_LOOKUP_RETRIES)
417     {
418         if(!initialized_)
419         {
420             return false;
421         }
422
423         if(scans < maxScans)
424         {
425             if(cm.getBestConnection(best, lookupType))
426             {
427                 found = true;
428             }
429
430             scans++;
431         }
432
433         // If there is only gprs connection available,
434         // make sure that we are on 3g network
435         if(found && (best.type != ConnectionManager::GPRS || is3g()))
436         {
437             break;
438         }
439
440         if(found)
441         {
442             sleep(WAIT_BETWEEN_RETRIES);
443         }
444
445         qDebug() << "No connections found, retrying...";
446
447         cretries++;
448
449     }
450
451     if(cretries >= CONNECTION_LOOKUP_RETRIES)
452     {
453         showError(tr("No available 3G or WLAN networks found."));
454         return false;
455     }
456
457     int retries = 0;
458
459     while(retries < CONNECT_RETRIES)
460     {
461         if(!initialized_)
462         {
463             return false;
464         }
465
466         qDebug() << "Connecting to " << best.name;
467
468         if(cm.connect(best.id))
469         {
470             break;
471         }
472         else if(cm.error() == ConnectionManager::INVALID_IAP)
473         {
474             showError(tr("Selected access point doesn't exist."));
475             return false;
476         }
477
478         sleep(WAIT_BETWEEN_RETRIES);
479
480         qDebug() << "Unable to connect, retrying...";
481         retries++;
482
483     }
484
485     if(initialized_ && retries >= CONNECT_RETRIES)
486     {
487         showError(tr("Unable to connect to network."));
488         return false;
489     }
490
491     return initialized_;
492 }
493
494 void CallListener::showError(QString const& msg, int timeout)
495 {
496     qDebug() << "Error: " << msg;
497
498     if(!initialized_ || !box_)
499     {
500         return;
501     }
502
503     box_->setTimeout(ERROR_BANNER_TIMEOUT);
504
505     if(timeout)
506     {
507         showDelayedResult(msg, timeout);
508     }
509     else
510     {
511         showResult(msg);
512     }
513 }
514
515 bool CallListener::is3g()
516 {
517     QDBusMessage msg = QDBusMessage::createMethodCall("com.nokia.phone.net",
518                                                       "/com/nokia/phone/net",
519                                                       "Phone.Net",
520                                                       "get_registration_status");
521
522     QDBusMessage rep = systemBus_.call(msg);
523
524     if(rep.type() == QDBusMessage::ErrorMessage)
525     {
526         qDebug() << "Unable to get network status";
527         return false;
528     }
529
530     uint status = rep.arguments().value(6).toUInt();
531
532     if(status & 0x10) // 3.5G (HSUPA)
533     {
534         return true;
535     }
536     else if(status & 0x08) // 3G (HSDPA)
537     {
538         return true;
539     }
540
541     // Something else
542     return false;
543 }
544
545 void CallListener::timerEvent(QTimerEvent* event)
546 {
547     Q_UNUSED(event);
548     killTimer(timer_);
549     timer_ = 0;
550 }
551
552 void CallListener::sleep(int ms)
553 {
554     if(timer_)
555     {
556         killTimer(timer_);
557     }
558
559     timer_ = startTimer(ms);
560
561     while(timer_)
562     {
563         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
564     }
565 }