---
Makefile.am | 3 +
plugins/callcounters.c | 388 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 391 insertions(+), 0 deletions(-)
create mode 100644 plugins/callcounters.c
diff --git a/Makefile.am b/Makefile.am
index f841b4c..7ae4a74 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -295,6 +295,9 @@ builtin_modules += example_nettime
builtin_sources += examples/nettime.c
endif
+builtin_modules += cc
+builtin_sources += plugins/callcounters.c
+
builtin_modules += smart_messaging
builtin_sources += plugins/smart-messaging.c
diff --git a/plugins/callcounters.c b/plugins/callcounters.c
new file mode 100644
index 0000000..a1306e5
--- /dev/null
+++ b/plugins/callcounters.c
@@ -0,0 +1,388 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "storage.h"
+
+#define CC_TIMEOUT 10000
+#define CC_STORE "callcounters"
+#define CC_GROUP "CallCounters"
+#define CC_INTERFACE "org.ofono.CallCounters"
+
+struct call_item {
+ unsigned int id;
+ int dir;
+ time_t start_time;
+};
+
+struct call_counters {
+ struct ofono_history_context *context;
+ const char *imsi;
+ GKeyFile *file;
+ time_t ibase;
+ time_t obase;
+ time_t incoming;
+ time_t outgoing;
+ gint timeout_source;
+ GSList *calls;
+};
+
+static time_t cc_time()
+{
+ struct timespec now;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ return now.tv_sec;
+}
+
+static gboolean cc_no_calls(struct call_counters *cc)
+{
+ return cc->calls == NULL;
+}
+
+static struct call_item *cc_find_ci(struct call_counters *cc, unsigned int id)
+{
+ struct call_item *ci;
+ GSList *l;
+
+ for (l = cc->calls; l; l = l->next) {
+ ci = l->data;
+ if (ci->id == id)
+ return ci;
+ }
+
+ return NULL;
+}
+
+static void cc_load(struct call_counters *cc)
+{
+ struct ofono_modem *modem = cc->context->modem;
+ struct ofono_atom *atom;
+ struct ofono_sim *sim;
+ GError *error1 = NULL, *error2 = NULL;
+
+ cc->incoming = cc->ibase = 0;
+ cc->outgoing = cc->obase = 0;
+
+ atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_SIM);
+ sim = __ofono_atom_get_data(atom);
+
+ cc->imsi = ofono_sim_get_imsi(sim);
+ if (cc->imsi == NULL)
+ return;
+
+ cc->file = storage_open(cc->imsi, CC_STORE);
+ if (cc->file == NULL)
+ return;
+
+ cc->incoming = g_key_file_get_integer(cc->file, CC_GROUP,
+ "Incoming", &error1);
+
+ if (error1) {
+ g_key_file_set_integer(cc->file, CC_GROUP,
+ "Incoming", cc->incoming);
+ storage_sync(cc->imsi, CC_STORE, cc->file);
+ }
+
+ cc->ibase = cc->incoming;
+
+ cc->outgoing = g_key_file_get_integer(cc->file, CC_GROUP,
+ "Outgoing", &error2);
+
+ if (error2) {
+ g_key_file_set_integer(cc->file, CC_GROUP,
+ "Outgoing", cc->outgoing);
+ }
+
+ cc->obase = cc->outgoing;
+
+ if (error1 != NULL || error2 != NULL)
+ storage_sync(cc->imsi, CC_STORE, cc->file);
+}
+
+static void cc_save(struct call_counters *cc)
+{
+ if (cc->file == NULL)
+ return;
+
+ g_key_file_set_integer(cc->file, CC_GROUP,
+ "Incoming", cc->incoming);
+ g_key_file_set_integer(cc->file, CC_GROUP,
+ "Outgoing", cc->outgoing);
+ storage_sync(cc->imsi, CC_STORE, cc->file);
+}
+
+static void cc_close(struct call_counters *cc)
+{
+ if (cc->file)
+ storage_close(cc->imsi, CC_STORE, cc->file, TRUE);
+
+ cc->file = NULL;
+}
+
+static void cc_update(struct call_counters *cc, struct call_item *ci)
+{
+ time_t now = cc_time();
+ time_t idiff = 0, odiff = 0;
+ struct call_item *lci;
+ GSList *l;
+
+ for (l = cc->calls; l; l = l->next) {
+ lci = l->data;
+ if (lci->dir == CALL_DIRECTION_MOBILE_ORIGINATED) {
+ if (lci != ci)
+ odiff += now - lci->start_time;
+ else
+ cc->obase += now - lci->start_time;
+ } else {
+ if (lci != ci)
+ idiff += now - lci->start_time;
+ else
+ cc->ibase += now - lci->start_time;
+ }
+ }
+
+ cc->outgoing = cc->obase + odiff;
+ cc->incoming = cc->ibase + idiff;
+
+ cc_save(cc);
+}
+
+static gboolean cc_timeout(gpointer user_data)
+{
+ struct call_counters *cc = user_data;
+ cc_update(cc, NULL);
+ return TRUE;
+}
+
+static DBusMessage *cc_dbus_get(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct call_counters *cc = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ if (cc_no_calls(cc)) {
+ cc_load(cc);
+ cc_close(cc);
+ } else {
+ cc_update(cc, NULL);
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ OFONO_PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+ ofono_dbus_dict_append(&dict, "Incoming", DBUS_TYPE_UINT32,
+ &cc->incoming);
+ ofono_dbus_dict_append(&dict, "Outgoing", DBUS_TYPE_UINT32,
+ &cc->outgoing);
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *cc_dbus_clear(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct call_counters *cc = data;
+ DBusMessage *reply;
+
+ if (cc_no_calls(cc)) {
+ cc_load(cc);
+ cc->incoming = cc->ibase = 0;
+ cc->outgoing = cc->obase = 0;
+ cc_save(cc);
+ cc_close(cc);
+ reply = dbus_message_new_method_return(msg);
+ } else {
+ reply = __ofono_error_failed(msg);
+ }
+
+ return reply;
+}
+
+static GDBusMethodTable cc_methods[] = {
+ {"Get", "", "a{sv}", cc_dbus_get},
+ {"Clear", "", "", cc_dbus_clear},
+ { }
+};
+
+static void cc_dbus_register(struct call_counters *cc)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = ofono_modem_get_path(cc->context->modem);
+
+ if (!g_dbus_register_interface(conn, path,
+ CC_INTERFACE,
+ cc_methods, NULL, NULL,
+ cc, NULL)) {
+ ofono_error("Could not create %s interface", CC_INTERFACE);
+ return;
+ }
+
+ ofono_modem_add_interface(cc->context->modem, CC_INTERFACE);
+}
+
+static gboolean cc_dbus_unregister(struct call_counters *cc)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = ofono_modem_get_path(cc->context->modem);
+
+ ofono_modem_remove_interface(cc->context->modem, CC_INTERFACE);
+
+ return g_dbus_unregister_interface(conn, path, CC_INTERFACE);
+}
+
+static int cc_probe(struct ofono_history_context *context)
+{
+ struct call_counters *cc;
+
+ ofono_debug("Call Counters probe for modem: %p", context->modem);
+
+ cc = g_try_new0(struct call_counters, 1);
+ if (cc == NULL)
+ return -1;
+
+ context->data = cc;
+ cc->context = context;
+ cc_dbus_register(cc);
+
+ return 0;
+}
+
+static void cc_remove(struct ofono_history_context *context)
+{
+ struct call_counters *cc = context->data;
+
+ ofono_debug("Call Counters remove for modem: %p", context->modem);
+
+ cc_dbus_unregister(cc);
+
+ if (cc->timeout_source != 0)
+ g_source_remove(cc->timeout_source);
+
+ cc_update(cc, NULL);
+ g_slist_free(cc->calls);
+
+ if (cc->file)
+ storage_close(cc->imsi, CC_STORE, cc->file, TRUE);
+
+ g_free(context->data);
+}
+
+static void cc_call_started(struct ofono_history_context *context,
+ const struct ofono_call *call, time_t start)
+{
+ time_t now = cc_time();
+ struct call_counters *cc = context->data;
+ struct call_item *ci;
+
+ ofono_debug("Call started on modem: %p", context->modem);
+
+ if (call->type != 0)
+ return;
+
+ ci = g_try_new0(struct call_item, 1);
+ if (ci == NULL)
+ return;
+
+ if (cc_no_calls(cc))
+ cc_load(context->data);
+
+ ci->id = call->id;
+ ci->dir = call->direction;
+ ci->start_time = now;
+ cc->calls = g_slist_append(cc->calls, ci);
+
+ if (cc->timeout_source == 0)
+ cc->timeout_source = g_timeout_add(CC_TIMEOUT, cc_timeout, cc);
+}
+
+static void cc_call_ended(struct ofono_history_context *context,
+ const struct ofono_call *call,
+ time_t start, time_t end)
+{
+ struct call_counters *cc = context->data;
+ struct call_item *ci = cc_find_ci(cc, call->id);
+
+ ofono_debug("Call ended on modem: %p", context->modem);
+
+ if (call->type != 0)
+ return;
+
+ if (ci == NULL)
+ return;
+
+ cc_update(cc, ci);
+ cc->calls = g_slist_remove(cc->calls, ci);
+
+ if (cc_no_calls(cc)) {
+ if (cc->timeout_source != 0)
+ g_source_remove(cc->timeout_source);
+
+ cc->timeout_source = 0;
+
+ if (cc->file)
+ storage_close(cc->imsi, CC_STORE, cc->file, TRUE);
+
+ cc->file = NULL;
+ }
+}
+
+static struct ofono_history_driver cc_driver = {
+ .name = "Call Counters",
+ .probe = cc_probe,
+ .remove = cc_remove,
+ .call_started = cc_call_started,
+ .call_ended = cc_call_ended,
+};
+
+static int cc_init(void)
+{
+ return ofono_history_driver_register(&cc_driver);
+}
+
+static void cc_exit(void)
+{
+ ofono_history_driver_unregister(&cc_driver);
+}
+
+OFONO_PLUGIN_DEFINE(cc, "Call Counters plugin",
+ VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+ cc_init, cc_exit)
--
1.7.0.4