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