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