3 @author: Sudheer K. <scifi.guy@hotmail.com>
4 @license: GNU General Public License
7 #include "callrouter.h"
8 #include <dbusutility.h>
9 #include <gconfutility.h>
12 #include <QDBusConnection>
14 #include <QStringListIterator>
16 //Create a statis Dbus utility object that will be shared across all member functions
17 static DbusUtility dbusUtility = DbusUtility();
18 static QString strLastDialedNumber = QString();
20 CallRouter::CallRouter(QObject *parent) :
23 gconfUtility = new GConfUtility();
26 CallRouter::~CallRouter(){
31 void CallRouter::registerDBusService(){
32 QDBusConnection connection = dbusUtility.getConnection();
34 if (!connection.registerService(APPLICATION_DBUS_SERVICE)) {
35 qDebug() << dbusUtility.getErrorMessage();
39 if (!connection.registerObject(APPLICATION_DBUS_PATH, this,
40 QDBusConnection::ExportScriptableSlots)) {
41 qDebug() << dbusUtility.getErrorMessage();
45 this->connectToDBusSignals();
50 void CallRouter::unregisterDBusService(){
52 this->disconnectFromDBusSignals();
54 QDBusConnection connection = dbusUtility.getConnection();
56 connection.unregisterObject(APPLICATION_DBUS_PATH,QDBusConnection::UnregisterTree);
58 if (!connection.unregisterService(APPLICATION_DBUS_SERVICE)) {
59 qDebug() << dbusUtility.getErrorMessage();
65 void CallRouter::connectToDBusSignals(){
67 QDBusConnection connection = dbusUtility.getConnection();
69 // Connect to the signal to enable call routing
70 bool success = connection.connect(QString(""),QString(""),
71 APPLICATION_DBUS_INTERFACE,
72 QString("startOutgoingCallMonitor"),this,
73 SLOT(startOutgoingCallMonitor()));
76 qDebug() << "Successfully connected to Dbus signal org.maemo.vicar.startOutgoingCallMonitor";
79 qDebug() << "Failed to connect to Dbus signal org.maemo.vicar.startOutgoingCallMonitor";
80 qDebug() <<"DBus Error: "<< qPrintable(dbusUtility.getErrorMessage());
83 // Connect to the signal to disable call routing
84 success = connection.connect(QString(""),QString(""),
85 APPLICATION_DBUS_INTERFACE,
86 QString("stopOutgoingCallMonitor"),this,
87 SLOT(stopOutgoingCallMonitor()));
90 qDebug() << "Successfully connected to Dbus signal org.maemo.vicar.stopOutgoingCallMonitor";
93 qDebug() << "Failed to connect to Dbus signal org.maemo.vicar.stopOutgoingCallMonitor";
94 qDebug() <<"DBus Error: "<< qPrintable(dbusUtility.getErrorMessage());
99 void CallRouter::disconnectFromDBusSignals(){
101 QDBusConnection connection = dbusUtility.getConnection();
103 // Disconnect from the signal to enable call routing
104 bool success = connection.disconnect(QString(""),QString(""),
105 APPLICATION_DBUS_INTERFACE,
106 QString("startOutgoingCallMonitor"),this,
107 SLOT(startOutgoingCallMonitor()));
110 qDebug() << "Successfully disconnected from Dbus signal org.maemo.vicar.startOutgoingCallMonitor";
113 qDebug() << "Failed to disconnect from Dbus signal org.maemo.vicar.startOutgoingCallMonitor";
114 qDebug() <<"DBus Error: "<< qPrintable(dbusUtility.getErrorMessage());
117 // Disconnect from the signal to disable call routing
118 success = connection.connect(QString(""),QString(""),
119 APPLICATION_DBUS_INTERFACE,
120 QString("stopOutgoingCallMonitor"),this,
121 SLOT(stopOutgoingCallMonitor()));
124 qDebug() << "Successfully disconnected from Dbus signal org.maemo.vicar.stopOutgoingCallMonitor";
127 qDebug() << "Failed to disconnect from Dbus signal org.maemo.vicar.stopOutgoingCallMonitor";
128 qDebug() <<"DBus Error: "<< qPrintable(dbusUtility.getErrorMessage());
133 void CallRouter::startOutgoingCallMonitor(){
135 // Connect to DBus to monitor all outgoing calls
137 QDBusConnection connection = dbusUtility.getConnection();
140 // Declare the slot to be executed when new calls are placed
142 bool success = connection.connect(QString(""),
145 QString("CreateRequested"),this,
146 SLOT(processOutgoingCall(const QDBusMessage&)));
149 qDebug() << "Successfully connected to Dbus signal CreateRequested in interface "<< CSD_CALL_INTERFACE;
152 qDebug() << "Failed to connect to Dbus signal CreateRequested in interface " << CSD_CALL_INTERFACE;
153 qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
159 void CallRouter::stopOutgoingCallMonitor(){
161 this->stopCallStatusMonitors();
163 //Disconnect the slots from Dbus signals
164 QDBusConnection connection = dbusUtility.getConnection();
166 // Disconnect the slot for new calls
167 bool status = connection.disconnect(QString(""),
170 QString("CreateRequested"),this,
171 SLOT(processOutgoingCall(const QDBusMessage&)));
174 qDebug() << "Successfully disconnected from Dbus signal CreateRequested in interface "<< CSD_CALL_INTERFACE;
177 qDebug() << "Failed to disconnect from Dbus signal CreateRequested in interface "<< CSD_CALL_INTERFACE;
178 qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
183 void CallRouter::processOutgoingCall(const QDBusMessage& dbusMessage){
185 //Verify Whether Call Routing is Enabled
186 bool isRoutingEnabled = gconfUtility->getGconfValueBoolean("routing_enabled");
188 if (isRoutingEnabled){
189 //User is making a phone call. Get the phone number and verify if it is an international number
190 QList<QVariant> listArguments = dbusMessage.arguments();
191 QString strInternationalNumber = listArguments.first().toString();
193 qDebug() << "New Call Identified. Destination number is " << strInternationalNumber;
195 if (strInternationalNumber.startsWith("+") ||
196 strInternationalNumber.startsWith("00"))
198 qDebug() << "International number "<< strInternationalNumber << " recognized. Starting proceedings..";
200 //Check whether this is one of the excluded country codes
201 if (!isExcludedNumber(strInternationalNumber)){
203 //International number. Disconnect the current call (A new call will be placed)
205 //No arguments required to cancel the current call
206 QList<QVariant> argsToSend;
207 bool status = dbusUtility.sendMethodCall(CSD_CALL_SERVICE,
210 QString("Release"),argsToSend);
212 QString strUserMessage;
214 strUserMessage = QString("Routing international call via ").append(APPLICATION_FRIENDLY_NAME).append("..");
215 qDebug() << strUserMessage;
216 strLastDialedNumber = strInternationalNumber;
219 strUserMessage = QString("Call could not be cancelled.");
220 qDebug() << dbusUtility.getErrorMessage();
223 dbusUtility.displayNotification(strUserMessage);
225 //Wait for a few seconds before the current call is completely disconnected
226 QTimer *timer = new QTimer(this);
227 timer->setSingleShot(true);
228 connect(timer, SIGNAL(timeout()), this, SLOT(callViaCallingCard()));
235 void CallRouter::callViaCallingCard(){
236 //Now call the calling card number. This is generally a local and/or tollfree number
238 QString strCallingCardNumber = gconfUtility->getGconfValueString("calling_card_number");
240 qDebug() << "Wait time elapsed. Initiating call to "<< strCallingCardNumber;
242 QList<QVariant> argsToSend;
243 argsToSend.append(strCallingCardNumber);
244 argsToSend.append(0);
246 bool status = dbusUtility.sendMethodCall(CSD_SERVICE,
249 QString("CreateWith"),argsToSend);
251 QString strUserMessage;
253 qDebug() << "Call initiated successfully. Connecting DBus slot for audio connection monitor";
254 startCallStatusMonitors();
257 strUserMessage = QString("Unable to initiate new call to ").append(strCallingCardNumber);
258 qDebug() << dbusUtility.getErrorMessage();
259 strLastDialedNumber.clear();
262 dbusUtility.displayNotification(strUserMessage);
266 void CallRouter::startCallStatusMonitors(){
267 /* Declare the slot to be executed when a call is picked up by other party (Audio connection established).
268 We need this to confirm whether a call went though successfully.
271 QDBusConnection connection = dbusUtility.getConnection();
273 bool success = connection.connect(QString(""),
274 CSD_CALL_INSTANCE_PATH,
275 CSD_CALL_INSTANCE_INTERFACE,
276 QString("AudioConnect"),this,
277 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
280 qDebug() << "Successfully connected to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
283 qDebug() << "Failed to connect to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
284 qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
287 /* Declare the slot to be executed when the call is terminated (due to connection errors etc).
288 We need this to avoid sending DTMF code on wrong calls.
291 success = connection.connect(QString(""),
292 CSD_CALL_INSTANCE_PATH,
293 CSD_CALL_INSTANCE_INTERFACE,
294 QString("Terminated"),this,
295 SLOT(stopCallStatusMonitors()));
298 qDebug() << "Successfully connected to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
301 qDebug() << "Failed to connect to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
302 qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
305 /* Declare the slot to be executed when a call is received
306 (before we can place the call to calling card number).
307 It is extremely rare that somebody should get a call within these few seconds.
308 In any case, we need this to avoid sending DTMF code on the received call.
310 Btw - I don't care for the incoming number here. If anyone is calling the user before we can send DTMF code,
311 then we stop sending the DTMF code even if user does not respond to the call.
314 success = connection.connect(QString(""),
317 QString("Coming"),this,
318 SLOT(stopCallStatusMonitors()));
321 qDebug() << "Successfully connected to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
324 qDebug() << "Failed to connect to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
325 qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
329 void CallRouter::stopCallStatusMonitors(){
331 strLastDialedNumber.clear();
333 QDBusConnection connection = dbusUtility.getConnection();
335 // Disconnect the slot for audio connection status
336 bool status = connection.disconnect(QString(""),
337 CSD_CALL_INSTANCE_PATH,
338 CSD_CALL_INSTANCE_INTERFACE,
339 QString("AudioConnect"),this,
340 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
343 qDebug() << "Successfully disconnected from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
346 qDebug() << "Failed to disconnect from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
347 qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
350 // Disconnect the slot for monitoring terminated calls
351 status = connection.disconnect(QString(""),
352 CSD_CALL_INSTANCE_PATH,
353 CSD_CALL_INSTANCE_INTERFACE,
354 QString("Terminated"),this,
355 SLOT(stopCallStatusMonitors()));
358 qDebug() << "Successfully disconnected from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
361 qDebug() << "Failed to disconnect from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
362 qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
365 // Disconnect the slot for monitoring incoming calls
366 status = connection.disconnect(QString(""),
369 QString("Coming"),this,
370 SLOT(stopCallStatusMonitors()));
373 qDebug() << "Successfully disconnected from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
376 qDebug() << "Failed to disconnect from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
377 qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
381 void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
383 if (!strLastDialedNumber.isEmpty()){
384 //Verify whether we have the last dialed number available
386 QList<QVariant> listArguments = dbusMessage.arguments();
387 bool audioConnected = listArguments.first().toBool();
390 // Now that the call to Calling card number is successful. We can send the original number as DTMF tones
391 QString strDTMFCode = convertToDTMFCode(strLastDialedNumber);
393 qDebug() << "Audio connection established. Sending DTMF code "<< strDTMFCode;
395 QList<QVariant> argsToSend;
396 argsToSend.append(strDTMFCode);
398 bool status = dbusUtility.sendMethodCall(CSD_SERVICE,
401 QString("SendDTMF"),argsToSend);
404 QString strMessage = strDTMFCode.append(" sent as DTMF code");
405 qDebug() << strMessage;
406 dbusUtility.displayNotification(strMessage);
409 qDebug() << "Unable to send DTMF code.";
414 Connecting and Disconnecting from/to DBus signal for each international call
415 may not be the most efficient way of handling this. But we need to make sure
416 that the DTMF codes are sent only for the calls placed by this app (i.e calls to Calling card number).
419 qDebug() << "Now disconnecting from call status monitors..";
420 stopCallStatusMonitors();
424 qDebug() << "Audio not yet connected.";
429 qDebug() << "Last dialed number is empty.";
433 QString CallRouter::convertToDTMFCode(QString strNumber){
436 if (!strNumber.isEmpty()){
438 //Get the format required by calling card from coniguration
439 QString qstrDTMFFormat = gconfUtility->getGconfValueString("dtmf_format");
440 int intDTMFDelay = gconfUtility->getGconfValueInteger("dtmf_delay");
442 if (intDTMFDelay <1 ) intDTMFDelay = 1;
443 if (qstrDTMFFormat.isEmpty()) qstrDTMFFormat = "<Country Code><Area Code><Phone Number>";
445 //Add the prefix p so that there is some delay after the call is picked up by the automated system to send DTMF tones.
446 strDTMFCode = QString("").fill('p',intDTMFDelay);
448 /* Replace 00 (international dialing code) at the beginning
449 and also replace any character other than the numbers 0-9 and p.
451 QRegExp regexp = QRegExp("(^0{2})|[^0-9p]");
452 strNumber = strNumber.replace(regexp,"");
454 /* Now we have a clean number with only country code, area code and phone number,
455 lets convert it to the calling card friendly format
457 if (qstrDTMFFormat.startsWith("+")){
458 strDTMFCode = strDTMFCode.append("+");
460 else if (qstrDTMFFormat.startsWith("00")){
461 strDTMFCode = strDTMFCode.append("00");
463 else if (qstrDTMFFormat.startsWith("011")){
464 strDTMFCode = strDTMFCode.append("011");
466 //Default case - we don't need any prefix
468 strDTMFCode = strDTMFCode.append(strNumber);
470 //Now check whether we need a suffix
471 QString strDTMFSuffix = gconfUtility->getGconfValueString("dtmf_suffix");
472 if (!strDTMFSuffix.isEmpty() && !strDTMFSuffix.contains("--None--")){
473 strDTMFCode = strDTMFCode.append(strDTMFSuffix);
480 bool CallRouter::isExcludedNumber(QString strInternationalNumber){
482 bool isExcluded = false;
484 //Get the list of excluded codes
485 QString qstrExcludedNumbers = gconfUtility->getGconfValueString("numbers_to_exclude");
486 QStringList strExcludedCodeList = qstrExcludedNumbers.split(",");
487 QStringListIterator iterator(strExcludedCodeList);
489 QRegExp regexp = QRegExp("(^0{2})|[^0-9p]");
491 while (iterator.hasNext()){
492 QString strCode = iterator.next();
493 strCode = strCode.replace(regexp,"");
494 strInternationalNumber = strInternationalNumber.replace(regexp,"");
495 if (!strCode.isEmpty() && strInternationalNumber.startsWith(strCode)){