diff --git a/Makefile.am b/Makefile.am
index 1a3b0111c..6de5195f0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -577,7 +577,8 @@ src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) src/ofono.ver \
src/cdma-smsutil.h src/cdma-smsutil.c \
src/cdma-sms.c src/private-network.c src/cdma-netreg.c \
src/cdma-provision.c src/handsfree.c \
- src/sim-mnclength.c src/spn-table.c
+ src/sim-mnclength.c src/spn-table.c \
+ src/voicecallagent.c
src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ -ldl
@@ -713,6 +714,7 @@ test_scripts = test/backtrace \
test/hold-and-answer \
test/hangup-multiparty \
test/hangup-call \
+ test/test-voicecallagent \
test/rilmodem/test-hangup \
test/rilmodem/test-modem-offline \
test/rilmodem/test-sim-online
diff --git a/doc/voicecallmanager-api.txt b/doc/voicecallmanager-api.txt
index 34a9b25c5..5bb58071b 100644
--- a/doc/voicecallmanager-api.txt
+++ b/doc/voicecallmanager-api.txt
@@ -198,6 +198,25 @@ Methods dict GetProperties()
[service].Error.InvalidFormat
[service].Error.Failed
+ void RegisterVoicecallAgent(object path)
+
+ Registers an agent which will be called whenever a
+ specific voice call related event requiring a client
+ action occurs. Currently, the only such action is
+ playing a ringback tone locally.
+
+ Possible Errors: [service].Error.InProgress
+ [service].Error.InvalidArguments
+ [service].Error.InvalidFormat
+ [service].Error.Failed
+
+ void UnregisterVoicecallAgent(object path)
+
+ Unregisters an agent.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.Failed
+
Signals CallAdded(object path, dict properties)
Signal that is sent when a new call is added. It
@@ -257,3 +276,24 @@ Properties array{string} EmergencyNumbers [readonly]
of numbers provided by the specification and any
extra numbers provisioned by the carrier on the
SIM.
+
+VoiceCallAgent Hierarchy
+========================
+
+Service unique name
+Interface org.ofono.VoiceCallAgent
+Object path freely definable
+
+Methods void RingbackTone(boolean play_tone)
+
+ Requests the client to generate an alerting tone locally
+ (3GPP 24.008; 5.2.1.5). This can happen when an outgoing voice call
+ is made and network is not providing the alerting tone. True stands
+ for "start playing ringback tone" and False for "stop playing
+ ringback tone".
+
+ void Release() [noreply]
+
+ Agent is being released, possibly because of oFono terminating,
+ voicecall interface is being torn down or modem is switched off. No
+ UnregisterVoiceCallManagerAgent call is needed.
diff --git a/drivers/rilmodem/voicecall.c b/drivers/rilmodem/voicecall.c
index d63dd6160..daa26743b 100644
--- a/drivers/rilmodem/voicecall.c
+++ b/drivers/rilmodem/voicecall.c
@@ -653,6 +653,17 @@ static gboolean enable_supp_svc(gpointer user_data)
return FALSE;
}
+static void ril_ringback_tone_notify(struct ril_msg *message,
+ gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ gboolean playTone = g_ril_unsol_parse_ringback_tone(vd->ril, message);
+ DBG("play ringback tone: %d", playTone);
+ ofono_voicecall_ringback_tone_notify(vc, playTone);
+
+}
+
static gboolean ril_delayed_register(gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
@@ -670,6 +681,10 @@ static gboolean ril_delayed_register(gpointer user_data)
g_ril_register(vd->ril, RIL_UNSOL_SUPP_SVC_NOTIFICATION,
ril_ss_notify, vc);
+ /* Register for ringback tone notifications */
+ g_ril_register(vd->ril, RIL_UNSOL_RINGBACK_TONE,
+ ril_ringback_tone_notify, vc);
+
/* request supplementary service notifications*/
enable_supp_svc(vc);
diff --git a/gril/grilunsol.c b/gril/grilunsol.c
index e55623352..b02fc1ba9 100644
--- a/gril/grilunsol.c
+++ b/gril/grilunsol.c
@@ -577,3 +577,17 @@ struct unsol_ussd *g_ril_unsol_parse_ussd(GRil *gril, struct ril_msg *message)
return NULL;
}
+
+gboolean g_ril_unsol_parse_ringback_tone(GRil *gril,
+ struct ril_msg *message)
+{
+ struct parcel rilp;
+ gboolean playTone = FALSE;
+
+ g_ril_init_parcel(message, &rilp);
+ if (parcel_r_int32(&rilp) > 0)
+ playTone = parcel_r_int32(&rilp);
+
+ return playTone;
+}
+
diff --git a/gril/grilunsol.h b/gril/grilunsol.h
index a7ddfc942..9942fd21a 100644
--- a/gril/grilunsol.h
+++ b/gril/grilunsol.h
@@ -92,6 +92,8 @@ void g_ril_unsol_free_ussd(struct unsol_ussd *unsol);
struct unsol_ussd *g_ril_unsol_parse_ussd(GRil *gril, struct ril_msg *message);
+gboolean g_ril_unsol_parse_ringback_tone(GRil *gril, struct ril_msg *message);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/voicecall.h b/include/voicecall.h
index 221366e57..ee3d903f5 100644
--- a/include/voicecall.h
+++ b/include/voicecall.h
@@ -164,6 +164,8 @@ void ofono_voicecall_ssn_mt_notify(struct ofono_voicecall *vc, unsigned int id,
int code, int index,
const struct ofono_phone_number *ph);
+void ofono_voicecall_ringback_tone_notify(struct ofono_voicecall *vc,
+ const ofono_bool_t play_tone);
#ifdef __cplusplus
}
#endif
diff --git a/src/ofono.conf b/src/ofono.conf
index 8a83cd06b..16304bc49 100644
--- a/src/ofono.conf
+++ b/src/ofono.conf
@@ -14,6 +14,7 @@
+
diff --git a/src/voicecall.c b/src/voicecall.c
index ae76b91ff..7b8a8cc5d 100644
--- a/src/voicecall.c
+++ b/src/voicecall.c
@@ -3,6 +3,7 @@
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2014 Jolla Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -38,6 +39,7 @@
#include "simutil.h"
#include "smsutil.h"
#include "storage.h"
+#include "voicecallagent.h"
#define MAX_VOICE_CALLS 16
@@ -75,6 +77,7 @@ struct ofono_voicecall {
ofono_voicecall_cb_t release_queue_done_cb;
struct ofono_emulator *pending_em;
unsigned int pending_id;
+ struct voicecall_agent *vc_agent;
};
struct voicecall {
@@ -2140,6 +2143,72 @@ static DBusMessage *manager_get_calls(DBusConnection *conn,
return reply;
}
+static void voicecall_agent_notify(gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ vc->vc_agent = NULL;
+}
+
+static DBusMessage *voicecall_register_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_voicecall *vc = data;
+ const char *agent_path;
+
+ if (vc->vc_agent)
+ return __ofono_error_busy(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH,
+ &agent_path, DBUS_TYPE_INVALID) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ if (!__ofono_dbus_valid_object_path(agent_path))
+ return __ofono_error_invalid_format(msg);
+
+ vc->vc_agent = voicecall_agent_new(agent_path,
+ dbus_message_get_sender(msg));
+
+ if (vc->vc_agent == NULL)
+ return __ofono_error_failed(msg);
+
+ voicecall_agent_set_removed_notify(vc->vc_agent,
+ voicecall_agent_notify, vc);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *voicecall_unregister_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_voicecall *vc = data;
+ const char *agent_path;
+ const char *agent_bus = dbus_message_get_sender(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ if (vc->vc_agent == NULL)
+ return __ofono_error_failed(msg);
+
+ if (!voicecall_agent_matches(vc->vc_agent, agent_path, agent_bus))
+ return __ofono_error_access_denied(msg);
+
+ if (vc->vc_agent) {
+ voicecall_agent_free(vc->vc_agent);
+ vc->vc_agent = NULL;
+ }
+
+ return dbus_message_new_method_return(msg);
+}
+
+void ofono_voicecall_ringback_tone_notify(struct ofono_voicecall *vc,
+ const ofono_bool_t play_tone)
+{
+ if (vc->vc_agent)
+ voicecall_agent_ringback_tone(vc->vc_agent, play_tone);
+}
+
static const GDBusMethodTable manager_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -2172,6 +2241,12 @@ static const GDBusMethodTable manager_methods[] = {
{ GDBUS_METHOD("GetCalls",
NULL, GDBUS_ARGS({ "calls_with_properties", "a(oa{sv})" }),
manager_get_calls) },
+ { GDBUS_ASYNC_METHOD("RegisterVoicecallAgent",
+ GDBUS_ARGS({ "path", "o" }), NULL,
+ voicecall_register_agent) },
+ { GDBUS_ASYNC_METHOD("UnregisterVoicecallAgent",
+ GDBUS_ARGS({ "path", "o" }), NULL,
+ voicecall_unregister_agent) },
{ }
};
@@ -2704,6 +2779,11 @@ static void voicecall_unregister(struct ofono_atom *atom)
voicecall_close_settings(vc);
+ if (vc->vc_agent) {
+ voicecall_agent_free(vc->vc_agent);
+ vc->vc_agent = NULL;
+ }
+
if (vc->sim_state_watch) {
ofono_sim_remove_state_watch(vc->sim, vc->sim_state_watch);
vc->sim_state_watch = 0;
diff --git a/src/voicecallagent.c b/src/voicecallagent.c
new file mode 100644
index 000000000..9ef12b9a0
--- /dev/null
+++ b/src/voicecallagent.c
@@ -0,0 +1,133 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2014 Jolla Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include
+#include
+
+#include "ofono.h"
+#include "voicecallagent.h"
+
+#define OFONO_VOICECALL_AGENT_INTERFACE "org.ofono.VoiceCallAgent"
+
+struct voicecall_agent {
+ char *path; /* Agent Path */
+ char *bus; /* Agent bus */
+ guint disconnect_watch; /* DBus disconnect watch */
+ ofono_destroy_func removed_cb;
+ void *removed_data;
+};
+
+void voicecall_agent_send_release(struct voicecall_agent *agent);
+
+void voicecall_agent_ringback_tone(struct voicecall_agent *agent,
+ const ofono_bool_t play_tone)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ DBusMessage *message = dbus_message_new_method_call(
+ agent->bus, agent->path,
+ OFONO_VOICECALL_AGENT_INTERFACE,
+ "RingbackTone");
+
+ if (message == NULL)
+ return;
+
+ if (!dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &play_tone,
+ DBUS_TYPE_INVALID))
+ return;
+
+ dbus_message_set_no_reply(message, TRUE);
+ g_dbus_send_message(conn, message);
+}
+
+void voicecall_agent_send_release(struct voicecall_agent *agent)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ DBusMessage *message = dbus_message_new_method_call(
+ agent->bus, agent->path,
+ OFONO_VOICECALL_AGENT_INTERFACE,
+ "Release");
+
+ if (message == NULL)
+ return;
+
+ dbus_message_set_no_reply(message, TRUE);
+ g_dbus_send_message(conn, message);
+}
+
+void voicecall_agent_set_removed_notify(struct voicecall_agent *agent,
+ ofono_destroy_func destroy,
+ void *user_data)
+{
+ agent->removed_cb = destroy;
+ agent->removed_data = user_data; /* voicecall atom (not owned) */
+}
+
+void voicecall_agent_free(struct voicecall_agent *agent)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ if (agent->disconnect_watch) {
+ voicecall_agent_send_release(agent);
+ g_dbus_remove_watch(conn, agent->disconnect_watch);
+ agent->disconnect_watch = 0;
+ }
+
+ if (agent->removed_cb)
+ agent->removed_cb(agent->removed_data);
+
+ g_free(agent->path);
+ g_free(agent->bus);
+ g_free(agent);
+}
+
+ofono_bool_t voicecall_agent_matches(struct voicecall_agent *agent,
+ const char *path, const char *sender)
+{
+ return g_str_equal(agent->path, path) &&
+ g_str_equal(agent->bus, sender);
+}
+
+void voicecall_agent_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+ struct voicecall_agent *agent = user_data;
+
+ agent->disconnect_watch = 0;
+ voicecall_agent_free(agent);
+}
+
+struct voicecall_agent *voicecall_agent_new(const char *path,
+ const char *sender)
+{
+ struct voicecall_agent *agent = g_try_new0(struct voicecall_agent, 1);
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ if (agent == NULL)
+ return NULL;
+
+ agent->path = g_strdup(path);
+ agent->bus = g_strdup(sender);
+
+ agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, sender,
+ voicecall_agent_disconnect_cb,
+ agent, NULL);
+
+ return agent;
+}
diff --git a/src/voicecallagent.h b/src/voicecallagent.h
new file mode 100644
index 000000000..0cd07fc82
--- /dev/null
+++ b/src/voicecallagent.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2014 Jolla Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct voicecall_agent;
+
+void voicecall_agent_ringback_tone(struct voicecall_agent *agent,
+ const ofono_bool_t play_tone);
+
+void voicecall_agent_set_removed_notify(struct voicecall_agent *agent,
+ ofono_destroy_func removed_cb,
+ void *user_data);
+
+void voicecall_agent_free(struct voicecall_agent *agent);
+
+ofono_bool_t voicecall_agent_matches(struct voicecall_agent *agent,
+ const char *path, const char *sender);
+
+struct voicecall_agent *voicecall_agent_new(const char *path,
+ const char *sender);
+
+void voicecall_agent_disconnect_cb(DBusConnection *conn,
+ void *user_data);
diff --git a/test/test-voicecallagent b/test/test-voicecallagent
new file mode 100755
index 000000000..15caa38a8
--- /dev/null
+++ b/test/test-voicecallagent
@@ -0,0 +1,48 @@
+#!/usr/bin/python3
+
+from gi.repository import GLib
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class VoiceCallAgent(dbus.service.Object):
+ @dbus.service.method("org.ofono.VoiceCallAgent",
+ in_signature="", out_signature="")
+ def Release(self):
+ print("Agent got Release")
+ mainloop.quit()
+
+ @dbus.service.method("org.ofono.VoiceCallAgent",
+ in_signature="b", out_signature="")
+ def RingbackTone(self, play_tone):
+ print("Agent got play_tone notification: %d" % play_tone)
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+ manager = dbus.Interface(bus.get_object("org.ofono", "/"),
+ "org.ofono.Manager")
+
+ modems = manager.GetModems()
+
+ for path, properties in modems:
+ if "org.ofono.VoiceCallManager" not in properties["Interfaces"]:
+ continue
+
+ vcm = dbus.Interface(bus.get_object('org.ofono', path),
+ 'org.ofono.VoiceCallManager')
+
+ path = "/test/agent"
+ agent = VoiceCallAgent(bus, path)
+ vcm.RegisterVoicecallAgent(agent)
+ print("Agent registered")
+ mainloop = GLib.MainLoop()
+
+ try:
+ mainloop.run()
+ except KeyboardInterrupt:
+ vcm.UnregisterVoicecallAgent(path)
+ print("Agent unregistered (interrupt)")