summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/Makefile.am4
-rw-r--r--drivers/atmodem/at.h3
-rw-r--r--drivers/atmodem/atmodem.c2
-rw-r--r--drivers/atmodem/phonebook.c303
-rw-r--r--src/Makefile.am3
-rw-r--r--src/driver.h29
-rw-r--r--src/modem.h1
-rw-r--r--src/phonebook.c318
-rwxr-xr-xtest/test-phonebook30
9 files changed, 690 insertions, 3 deletions
diff --git a/drivers/Makefile.am b/drivers/Makefile.am
index 9559ed5f..010e0d5a 100644
--- a/drivers/Makefile.am
+++ b/drivers/Makefile.am
@@ -10,13 +10,13 @@ builtin_sources += atmodem/atmodem.c atmodem/at.h \
atmodem/call-forwarding.c atmodem/call-meter.c \
atmodem/network-registration.c atmodem/sim.c \
atmodem/ussd.c atmodem/voicecall.c \
- atmodem/call-barring.c
+ atmodem/call-barring.c atmodem/phonebook.c
builtin_modules += phonet
builtin_sources += phonet/phonet.c
noinst_LTLIBRARIES = libbuiltin.la
-
+
libbuiltin_la_SOURCES = $(builtin_sources)
libbuiltin_la_LDFLAGS =
libbuiltin_la_CFLAGS = $(AM_CFLAGS) $(builtin_cflags) \
diff --git a/drivers/atmodem/at.h b/drivers/atmodem/at.h
index ec7fb80c..03002822 100644
--- a/drivers/atmodem/at.h
+++ b/drivers/atmodem/at.h
@@ -94,3 +94,6 @@ extern void at_sim_exit(struct ofono_modem *modem);
extern void at_sms_init(struct ofono_modem *modem);
extern void at_sms_exit(struct ofono_modem *modem);
+
+extern void at_phonebook_init(struct ofono_modem *modem);
+extern void at_phonebook_exit(struct ofono_modem *modem);
diff --git a/drivers/atmodem/atmodem.c b/drivers/atmodem/atmodem.c
index 20b95a5f..51d0636a 100644
--- a/drivers/atmodem/atmodem.c
+++ b/drivers/atmodem/atmodem.c
@@ -101,6 +101,7 @@ static void interface_exit(struct at_data *at)
at_call_barring_exit(at->modem);
at_ussd_exit(at->modem);
at_sim_exit(at->modem);
+ at_phonebook_exit(at->modem);
}
static void manager_free(gpointer user)
@@ -351,6 +352,7 @@ static void create_cb(GIOChannel *io, gboolean success, gpointer user)
at_call_meter_init(at->modem);
at_call_barring_init(at->modem);
at_sms_init(at->modem);
+ at_phonebook_init(at->modem);
at->io = io;
at->driver = g_strdup(driver);
diff --git a/drivers/atmodem/phonebook.c b/drivers/atmodem/phonebook.c
new file mode 100644
index 00000000..d03b352c
--- /dev/null
+++ b/drivers/atmodem/phonebook.c
@@ -0,0 +1,303 @@
+/*
+ * oFono - GSM Telephony Stack for Linux
+ *
+ * Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include "driver.h"
+#include "util.h"
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "at.h"
+
+
+#define INDEX_INVALID -1
+
+static const char *none_prefix[] = { NULL };
+static const char *entries_prefix[] = { "+CPBR:", NULL };
+
+static void at_read_entries_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_phonebook_export_entries_t cb = cbd->cb;
+ struct ofono_error error;
+ GAtResultIter iter;
+ int num_entries_max = 0;
+ int num_entries_real;
+ int num;
+ struct ofono_phonebook_entry *entries;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+ if (!ok) {
+ cb(&error, 0, NULL, cbd->data);
+ return;
+ }
+
+ g_at_result_iter_init(&iter, result);
+
+ while (g_at_result_iter_next(&iter, "+CPBR:"))
+ num_entries_max += 1;
+
+ entries = g_new(struct ofono_phonebook_entry, num_entries_max);
+
+ g_at_result_iter_init(&iter, result);
+ for (num_entries_real = 0; g_at_result_iter_next(&iter, "+CPBR:");) {
+ int index, type, hidden, adtype;
+ const char *number, *text, *group, *adnumber,
+ *secondtext, *email, *sip_uri, *tel_uri;
+
+ if (!g_at_result_iter_next_number(&iter, &index))
+ continue;
+ entries[num_entries_real].index = index;
+
+ if (!g_at_result_iter_next_string(&iter, &number))
+ continue;
+ entries[num_entries_real].number = g_strdup(number);
+
+ if (!g_at_result_iter_next_number(&iter, &type)) {
+ g_free(entries[num_entries_real].number);
+ continue;
+ }
+ entries[num_entries_real].type = type;
+
+ if (!g_at_result_iter_next_string(&iter, &text)) {
+ g_free(entries[num_entries_real].number);
+ continue;
+ }
+ entries[num_entries_real].text = g_strdup(text);
+
+ if (!g_at_result_iter_next_number(&iter, &hidden))
+ hidden = -1;
+ entries[num_entries_real].hidden = hidden;
+
+ if (!g_at_result_iter_next_string(&iter, &group))
+ group = NULL;
+ entries[num_entries_real].group = g_strdup(group);
+
+ if (!g_at_result_iter_next_string(&iter, &adnumber))
+ adnumber = NULL;
+ entries[num_entries_real].adnumber = g_strdup(adnumber);
+
+ if (!g_at_result_iter_next_number(&iter, &adtype))
+ adtype = -1;
+ entries[num_entries_real].adtype = adtype;
+
+ if (!g_at_result_iter_next_string(&iter, &secondtext))
+ secondtext = NULL;
+ entries[num_entries_real].secondtext = g_strdup(secondtext);
+
+ if (!g_at_result_iter_next_string(&iter, &email))
+ email = NULL;
+ entries[num_entries_real].email = g_strdup(email);
+
+ if (!g_at_result_iter_next_string(&iter, &sip_uri))
+ sip_uri = NULL;
+ entries[num_entries_real].sip_uri = g_strdup(sip_uri);
+
+ if (!g_at_result_iter_next_string(&iter, &tel_uri))
+ tel_uri = NULL;
+ entries[num_entries_real].tel_uri = g_strdup(tel_uri);
+
+ num_entries_real++;
+ }
+ cb(&error, num_entries_real, entries, cbd->data);
+
+ for (num = 0; num < num_entries_real; num++) {
+ g_free(entries[num].number);
+ g_free(entries[num].text);
+ g_free(entries[num].group);
+ g_free(entries[num].adnumber);
+ g_free(entries[num].secondtext);
+ g_free(entries[num].email);
+ g_free(entries[num].sip_uri);
+ g_free(entries[num].tel_uri);
+ }
+ g_free(entries);
+ entries = NULL;
+}
+
+static void at_read_entries(struct ofono_modem *modem, int index_min,
+ int index_max, ofono_phonebook_export_entries_t cb,
+ void *data)
+{
+ struct at_data *at = ofono_modem_userdata(modem);
+ struct cb_data *cbd = cb_data_new(modem, cb, data);
+ char buf[32];
+
+ if (!cbd)
+ goto error;
+
+ sprintf(buf, "AT+CPBR=%d,%d", index_min, index_max);
+ if (g_at_chat_send(at->parser, buf, entries_prefix,
+ at_read_entries_cb, cbd, g_free) > 0)
+ return;
+
+error:
+ if (cbd)
+ g_free(cbd);
+
+ {
+ DECLARE_FAILURE(error);
+ cb(&error, 0, NULL, data);
+ }
+}
+
+static void at_list_indices_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_modem *modem = cbd->modem;
+ ofono_phonebook_export_entries_t cb = cbd->cb;
+ struct ofono_error error;
+ GAtResultIter iter;
+ const char *indices;
+ int index_min = 0, index_max = 0;
+ int i = 0, integer_index = 0;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+ if (!ok) {
+ cb(&error, 0, NULL, cbd->data);
+ return;
+ }
+
+ g_at_result_iter_init(&iter, result);
+ if (!g_at_result_iter_next(&iter, "+CPBR:")) {
+ DECLARE_FAILURE(e);
+ cb(&e, 0, NULL, cbd->data);
+ return;
+ }
+
+ indices = g_at_result_iter_raw_line(&iter);
+ /* retrieve index_min and index_max from indices
+ * which seems like "(1-150),32,16"
+ */
+ while (indices[i] != ')') {
+ if (indices[i] == '(')
+ integer_index = 1;
+ else if (indices[i] == '-')
+ integer_index = 2;
+ if ((indices[i] >= '0') && (indices[i] <= '9')) {
+ if (integer_index == 1)
+ index_min = index_min*10 + (indices[i] - '0');
+ else if (integer_index == 2)
+ index_max = index_max*10 + (indices[i] - '0');
+ }
+ i++;
+ }
+
+ at_read_entries(modem, index_min, index_max, cb, modem);
+}
+
+static void at_list_indices(struct ofono_modem *modem,
+ ofono_phonebook_export_entries_t cb, void *data)
+{
+ struct at_data *at = ofono_modem_userdata(modem);
+ struct cb_data *cbd = cb_data_new(modem, cb, data);
+
+ if (!cbd)
+ goto error;
+
+ if (g_at_chat_send(at->parser, "AT+CPBR=?", entries_prefix,
+ at_list_indices_cb, cbd, g_free) > 0)
+ return;
+
+error:
+ if (cbd)
+ g_free(cbd);
+
+ {
+ DECLARE_FAILURE(error);
+ cb(&error, 0, NULL, data);
+ }
+}
+
+static void at_select_storage_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_modem *modem = cbd->modem;
+ ofono_phonebook_export_entries_t cb = cbd->cb;
+ struct ofono_error error;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+ if (!ok) {
+ cb(&error, 0, NULL, cbd->data);
+ return;
+ } else
+ at_list_indices(modem, cb, modem);
+}
+
+static void at_select_storage(struct ofono_modem *modem, char *storage,
+ ofono_phonebook_export_entries_t cb, void *data)
+{
+ struct at_data *at = ofono_modem_userdata(modem);
+ struct cb_data *cbd = cb_data_new(modem, cb, data);
+ char buf[32];
+
+ if (!cbd)
+ goto error;
+
+ sprintf(buf, "AT+CPBS=%s", storage);
+ if (g_at_chat_send(at->parser, buf, none_prefix,
+ at_select_storage_cb, cbd, g_free) > 0)
+ return;
+
+error:
+ if (cbd)
+ g_free(cbd);
+
+ {
+ DECLARE_FAILURE(error);
+ cb(&error, 0, NULL, data);
+ }
+}
+
+static void at_export_entries(struct ofono_modem *modem, char *storage,
+ ofono_phonebook_export_entries_t cb, void *data)
+{
+ at_select_storage(modem, storage, cb, modem);
+}
+
+static struct ofono_phonebook_ops ops = {
+ .export_entries = at_export_entries
+};
+
+void at_phonebook_init(struct ofono_modem *modem)
+{
+ ofono_phonebook_register(modem, &ops);
+}
+
+void at_phonebook_exit(struct ofono_modem *modem)
+{
+ ofono_phonebook_unregister(modem);
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index 7548d8ef..fd42cf4c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,7 +12,8 @@ ofonod_SOURCES = main.c ofono.h log.c plugin.c \
manager.c dbus-gsm.h dbus-gsm.c util.h util.c \
network.c voicecall.c ussd.h ussd.c sms.c \
call-settings.c call-forwarding.c call-meter.c \
- smsutil.h smsutil.c cssn.h cssn.c call-barring.c sim.h sim.c
+ smsutil.h smsutil.c cssn.h cssn.c call-barring.c sim.h sim.c \
+ phonebook.c
ofonod_LDADD = $(top_builddir)/plugins/libbuiltin.la \
$(top_builddir)/drivers/libbuiltin.la \
diff --git a/src/driver.h b/src/driver.h
index 99969a58..50ff735b 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -102,6 +102,21 @@ struct ofono_own_number {
int itc;
};
+struct ofono_phonebook_entry {
+ int index;
+ char *number;
+ int type;
+ char *text;
+ int hidden;
+ char *group;
+ char *adnumber;
+ int adtype;
+ char *secondtext;
+ char *email;
+ char *sip_uri;
+ char *tel_uri;
+};
+
/* Notification functions, the integer values here should map to
* values obtained from the modem. The enumerations are the same
* as the values for the fields found in 3GPP TS 27.007
@@ -177,6 +192,10 @@ typedef void (*ofono_sca_query_cb_t)(const struct ofono_error *error,
const struct ofono_phone_number *ph,
void *data);
+typedef void (*ofono_phonebook_export_entries_t)(
+ const struct ofono_error *error, int num_entries,
+ const struct ofono_phonebook_entry *entries, void *data);
+
struct ofono_modem_attribute_ops {
void (*query_manufacturer)(struct ofono_modem *modem,
ofono_modem_attribute_query_cb_t cb, void *data);
@@ -397,3 +416,13 @@ void ofono_sms_deliver_notify(struct ofono_modem *modem, unsigned char *pdu,
int len, int tpdu_len);
void ofono_sms_status_notify(struct ofono_modem *modem, unsigned char *pdu,
int len, int tpdu_len);
+
+struct ofono_phonebook_ops {
+ void (*export_entries)(struct ofono_modem *modem, char *storage,
+ ofono_phonebook_export_entries_t cb, void *data);
+};
+
+int ofono_phonebook_register(struct ofono_modem *modem,
+ struct ofono_phonebook_ops *ops);
+void ofono_phonebook_unregister(struct ofono_modem *modem);
+
diff --git a/src/modem.h b/src/modem.h
index 22655f7a..c643de45 100644
--- a/src/modem.h
+++ b/src/modem.h
@@ -41,6 +41,7 @@ struct ofono_modem {
struct cssn_data *cssn;
struct sim_manager_data *sim_manager;
struct sms_manager_data *sms_manager;
+ struct phonebook_data *phonebook;
};
struct ofono_modem *modem_create(int id, struct ofono_modem_attribute_ops *ops);
diff --git a/src/phonebook.c b/src/phonebook.c
new file mode 100644
index 00000000..ae62286e
--- /dev/null
+++ b/src/phonebook.c
@@ -0,0 +1,318 @@
+/*
+ * oFono - GSM Telephony Stack for Linux
+ *
+ * Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "dbus-gsm.h"
+#include "modem.h"
+#include "driver.h"
+#include "common.h"
+
+
+#define PHONEBOOK_SERVICES_INTERFACE "org.ofono.Phonebook"
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define LEN_MAX 128
+#define TYPE_INTERNATIONAL 145
+
+struct phonebook_data {
+ struct ofono_phonebook_ops *ops;
+ DBusMessage *pending;
+ int storage_support_index; /* go through all supported storage */
+ int cached;
+ GString entries_vcard; /* entries with vcard 3.0 format */
+};
+
+static DBusMessage *export_entries_one_storage(DBusConnection *conn,
+ DBusMessage *msg, void *data);
+
+static const char *storage_support[] = { "\"SM\"", "\"ME\"", NULL };
+
+static struct phonebook_data *phonebook_create()
+{
+ struct phonebook_data *phonebook;
+ phonebook = g_try_new0(struct phonebook_data, 1);
+ return phonebook;
+}
+
+static void phonebook_destroy(gpointer data)
+{
+ struct ofono_modem *modem = data;
+ struct phonebook_data *phonebook = modem->phonebook;
+ g_free(phonebook);
+ modem->phonebook = NULL;
+}
+
+/* according to RFC 2425, the output string may need folding */
+static void vcard_printf(GString *str, const char *fmt, ...)
+{
+ char buf[1024];
+ va_list ap;
+ int len_temp, line_number, i;
+ unsigned int line_delimit = 75;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ line_number = strlen(buf) / line_delimit + 1;
+
+ for (i = 0; i < line_number; i++) {
+ len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i);
+ g_string_append_len(str, buf + line_delimit * i, len_temp);
+ if (i != line_number - 1)
+ g_string_append(str, "\r\n ");
+ }
+
+ g_string_append(str, "\r\n");
+}
+
+/* According to RFC 2426, we need escape following characters:
+ * '\n', '\r', ';', ',', '\'.
+ */
+static void add_slash(char *dest, char *src, int len_max, int len)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < len && j < len_max; i++, j++) {
+ switch (src[i]) {
+ case '\n':
+ dest[j++] = '\\';
+ dest[j] = 'n';
+ break;
+ case '\r':
+ dest[j++] = '\\';
+ dest[j] = 'r';
+ break;
+ case '\\':
+ case ';':
+ case ',':
+ dest[j++] = '\\';
+ default:
+ dest[j] = src[i];
+ break;
+ }
+ }
+ dest[j] = 0;
+ return;
+}
+
+static void vcard_printf_number(GString *entries_vcard_pointer, int type,
+ char *number, int prefer)
+{
+ char *pref = "", *intl = "";
+ char buf[128];
+ if (prefer)
+ pref = "PREF,";
+ if ((type == TYPE_INTERNATIONAL) && (number[0] != '+'))
+ intl = "+";
+
+ sprintf(buf, "TEL;TYPE=\%sVOICE:\%s\%s", pref, intl, number);
+ vcard_printf(entries_vcard_pointer, buf, number);
+}
+
+
+static void entry_to_vcard(GString *entries_vcard_pointer,
+ struct ofono_phonebook_entry *entry)
+{
+ char field[LEN_MAX];
+ vcard_printf(entries_vcard_pointer, "BEGIN:VCARD");
+ vcard_printf(entries_vcard_pointer, "VERSION:3.0");
+ add_slash(field, entry->text, LEN_MAX, strlen(entry->text));
+ vcard_printf(entries_vcard_pointer, "FN:%s", field);
+ vcard_printf_number(entries_vcard_pointer, entry->type,
+ entry->number, 1);
+ if (entry->group) {
+ add_slash(field, entry->group, LEN_MAX, strlen(entry->group));
+ vcard_printf(entries_vcard_pointer, "CATEGORIES:%s", field);
+ }
+ if (entry->adtype && entry->adnumber) {
+ vcard_printf_number(entries_vcard_pointer, entry->adtype,
+ entry->adnumber, 0);
+ }
+ if (entry->email) {
+ add_slash(field, entry->email, LEN_MAX, strlen(entry->email));
+ vcard_printf(entries_vcard_pointer,
+ "EMAIL;TYPE=INTERNET:%s", field);
+ }
+ if (entry->sip_uri) {
+ add_slash(field, entry->sip_uri, LEN_MAX,
+ strlen(entry->sip_uri));
+ vcard_printf(entries_vcard_pointer, "IMPP;TYPE=SIP:%s", field);
+ }
+ vcard_printf(entries_vcard_pointer, "END:VCARD");
+ vcard_printf(entries_vcard_pointer, "");
+}
+
+static DBusMessage *generate_export_entries_reply(struct ofono_modem *modem)
+{
+ struct phonebook_data *phonebook = modem->phonebook;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ reply = dbus_message_new_method_return(phonebook->pending);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &(phonebook->entries_vcard));
+ g_dbus_send_message(conn, reply);
+ return NULL;
+}
+
+static void export_entries_one_storage_cb(const struct ofono_error *error,
+ int num_entries, const struct ofono_phonebook_entry *entries, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct phonebook_data *phonebook = modem->phonebook;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ ofono_error("export_entries_one_storage_cb with %s failed",
+ storage_support[phonebook->storage_support_index]);
+ else {
+ int num = 0;
+ GString *entries_vcard_pointer = &(phonebook->entries_vcard);
+ for (num = 0; num < num_entries; num++)
+ entry_to_vcard(entries_vcard_pointer,
+ (struct ofono_phonebook_entry *)&entries[num]);
+ }
+
+ phonebook->storage_support_index++;
+ export_entries_one_storage(conn, phonebook->pending, modem);
+ return;
+}
+
+static DBusMessage *export_entries_one_storage(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct phonebook_data *phonebook = modem->phonebook;
+
+ if (storage_support[phonebook->storage_support_index] != NULL)
+ phonebook->ops->export_entries(modem,
+ (char *)storage_support[phonebook->storage_support_index],
+ export_entries_one_storage_cb, modem);
+ else {
+ phonebook->cached = 1;
+ generate_export_entries_reply(modem);
+ dbus_message_unref(phonebook->pending);
+ phonebook->pending = NULL;
+ }
+
+ return NULL;
+}
+
+static DBusMessage *export_entries(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct phonebook_data *phonebook = modem->phonebook;
+
+ if (phonebook->pending) {
+ DBusMessage *reply;
+ reply = dbus_gsm_busy(phonebook->pending);
+ g_dbus_send_message(conn, reply);
+ return NULL;
+ }
+ phonebook->pending = dbus_message_ref(msg);
+
+ if (phonebook->cached) {
+ generate_export_entries_reply(modem);
+ dbus_message_unref(phonebook->pending);
+ phonebook->pending = NULL;
+ return NULL;
+ }
+
+ g_string_set_size(&(phonebook->entries_vcard), 0);
+ phonebook->storage_support_index = 0;
+ export_entries_one_storage(conn, msg, data);
+ return NULL;
+}
+
+static GDBusMethodTable phonebook_methods[] = {
+ { "ExportEntries", "", "s", export_entries,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable phonebook_signals[] = {
+ { }
+};
+
+int ofono_phonebook_register(struct ofono_modem *modem,
+ struct ofono_phonebook_ops *ops)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (modem == NULL)
+ return -1;
+
+ if (ops == NULL)
+ return -1;
+
+ modem->phonebook = phonebook_create();
+
+ if (modem->phonebook == NULL)
+ return -1;
+
+ modem->phonebook->ops = ops;
+
+ if (!g_dbus_register_interface(conn, modem->path,
+ PHONEBOOK_SERVICES_INTERFACE,
+ phonebook_methods, phonebook_signals,
+ NULL, modem, phonebook_destroy)) {
+ ofono_error("Could not register Phonebook %s", modem->path);
+
+ phonebook_destroy(modem->phonebook);
+
+ return -1;
+ }
+
+ modem_add_interface(modem, PHONEBOOK_SERVICES_INTERFACE);
+ return 0;
+}
+
+void ofono_phonebook_unregister(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (modem->phonebook == NULL)
+ return;
+
+ modem_remove_interface(modem, PHONEBOOK_SERVICES_INTERFACE);
+ g_dbus_unregister_interface(conn, modem->path,
+ PHONEBOOK_SERVICES_INTERFACE);
+}
diff --git a/test/test-phonebook b/test/test-phonebook
new file mode 100755
index 00000000..56422111
--- /dev/null
+++ b/test/test-phonebook
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+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')
+
+ try:
+ modems = manager.GetProperties()['Modems']
+ except dbus.DBusException, e:
+ print "Unable to get the Modems property %s" % e
+
+ phonebook = dbus.Interface(bus.get_object('org.ofono', modems[0]),
+ 'org.ofono.Phonebook')
+
+ print "export entries for the first time"
+ print phonebook.ExportEntries()
+ print "export entries for the second time, which should return from cache"
+ print phonebook.ExportEntries()
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()