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