From f00af4527000c02dfe1aee4b6afeadef82bd4f65 Mon Sep 17 00:00:00 2001 From: Yang Gu Date: Mon, 15 Jun 2009 16:18:48 +0800 Subject: [PATCH] Export phonebook as vCard 3.0 format --- drivers/Makefile.am | 4 +- drivers/atmodem/at.h | 3 + drivers/atmodem/atmodem.c | 2 + drivers/atmodem/phonebook.c | 303 ++++++++++++++++++++++++++++++++++ src/Makefile.am | 3 +- src/driver.h | 29 ++++ src/modem.h | 1 + src/phonebook.c | 318 ++++++++++++++++++++++++++++++++++++ test/test-phonebook | 30 ++++ 9 files changed, 690 insertions(+), 3 deletions(-) create mode 100644 drivers/atmodem/phonebook.c create mode 100644 src/phonebook.c create mode 100755 test/test-phonebook 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 +#endif + +#define _GNU_SOURCE +#include +#include +#include + +#include + +#include +#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 +#endif + +#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include + +#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()