/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 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 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 #endif #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "util.h" #include "simutil.h" #include "smsutil.h" struct mailbox_state { gboolean indication; unsigned char message_count; }; struct ofono_message_waiting { struct mailbox_state messages[5]; unsigned char efmwis_length; unsigned char efmbdn_length; unsigned char efmbdn_record_id[5]; unsigned int efmbdn_watch; unsigned char ef_cphs_mwis_length; unsigned char ef_cphs_mbdn_length; unsigned int ef_cphs_mbdn_watch; gboolean mbdn_not_provided; gboolean cphs_mbdn_not_provided; struct ofono_phone_number mailbox_number[5]; struct ofono_sim *sim; struct ofono_sim_context *sim_context; struct ofono_atom *atom; }; struct mbdn_set_request { struct ofono_message_waiting *mw; int mailbox; struct ofono_phone_number number; DBusMessage *msg; gboolean cphs; }; static const char *mw_message_waiting_property_name[5] = { "VoicemailWaiting", #if 0 "FaxWaiting", "EmailWaiting", "OtherWaiting", "VideomailWaiting", #endif }; static const char *mw_message_count_property_name[5] = { "VoicemailMessageCount", #if 0 "FaxMessageCount", "EmailMessageCount", "OtherMessageCount", "VideomailMessageCount", #endif }; static const char *mw_mailbox_property_name[5] = { "VoicemailMailboxNumber", #if 0 "FaxMailboxNumber", "EmailMailboxNumber", "OtherMailboxNumber", "VideomailMailboxNumber", #endif }; static const int mw_mailbox_to_cphs_record[5] = { 1, /* Line 1 mailbox */ 4, /* Fax mailbox */ 0, 3, /* Data mailbox */ 0, }; static void mbdn_set_cb(int ok, void *data); static DBusMessage *mw_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_message_waiting *mw = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; int i; dbus_bool_t indication; unsigned char count; const char *number; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); for (i = 0; i < 5; i++) { if (mw_message_waiting_property_name[i]) { indication = mw->messages[i].indication; ofono_dbus_dict_append(&dict, mw_message_waiting_property_name[i], DBUS_TYPE_BOOLEAN, &indication); } if (mw_message_count_property_name[i]) { count = mw->messages[i].message_count; ofono_dbus_dict_append(&dict, mw_message_count_property_name[i], DBUS_TYPE_BYTE, &count); } if (mw_mailbox_property_name[i]) { number = phone_number_to_string(&mw->mailbox_number[i]); ofono_dbus_dict_append(&dict, mw_mailbox_property_name[i], DBUS_TYPE_STRING, &number); } } dbus_message_iter_close_container(&iter, &dict); return reply; } static void cphs_mbdn_sync_cb(int ok, void *data) { struct mbdn_set_request *req = data; if (!ok) ofono_info("Failed to synchronize CPHS MBDN record"); g_free(req); } static DBusMessage *set_cphs_mbdn(struct ofono_message_waiting *mw, gboolean sync, int mailbox, const char *number, DBusMessage *msg) { struct mbdn_set_request *req; unsigned char efmbdn[255]; if ((mw->ef_cphs_mbdn_length && !mw_mailbox_to_cphs_record[mailbox]) || mw->cphs_mbdn_not_provided == TRUE) { if (msg) return __ofono_error_not_supported(msg); return NULL; } if (mw->ef_cphs_mbdn_length == 0) { if (msg) return __ofono_error_sim_not_ready(msg); return NULL; } req = g_new0(struct mbdn_set_request, 1); req->mw = mw; req->mailbox = mailbox; string_to_phone_number(number, &req->number); req->cphs = TRUE; sim_adn_build(efmbdn, req->mw->ef_cphs_mbdn_length, &req->number, NULL); if (ofono_sim_write(mw->sim_context, SIM_EF_CPHS_MBDN_FILEID, sync ? cphs_mbdn_sync_cb : mbdn_set_cb, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mailbox_to_cphs_record[mailbox], efmbdn, mw->ef_cphs_mbdn_length, req) == -1) { g_free(req); if (msg) return __ofono_error_failed(msg); } else req->msg = msg ? dbus_message_ref(msg) : NULL; return NULL; } static void mbdn_set_cb(int ok, void *data) { struct mbdn_set_request *req = data; struct ofono_phone_number *old = &req->mw->mailbox_number[req->mailbox]; const char *property; DBusMessage *reply = NULL; if (!ok) { if (req->msg) reply = __ofono_error_failed(req->msg); goto out; } if (req->msg) reply = dbus_message_new_method_return(req->msg); if (g_str_equal(req->number.number, old->number) && req->number.type == old->type) goto out; memcpy(old, &req->number, sizeof(struct ofono_phone_number)); property = mw_mailbox_property_name[req->mailbox]; if (property) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(req->mw->atom); const char *number; number = phone_number_to_string(old); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, property, DBUS_TYPE_STRING, &number); } /* * Make a single attempt at keeping the CPHS version of the file * in sync. */ if (req->cphs == FALSE) set_cphs_mbdn(req->mw, TRUE, req->mailbox, phone_number_to_string(&req->number), NULL); out: if (req->msg && reply) __ofono_dbus_pending_reply(&req->msg, reply); g_free(req); } static DBusMessage *set_mbdn(struct ofono_message_waiting *mw, int mailbox, const char *number, DBusMessage *msg) { struct mbdn_set_request *req; unsigned char efmbdn[255]; /* * If we have no 3GPP EFmbdn on the card, maybe the * CPHS version is available */ if ((mw->efmbdn_length > 0 && mw->efmbdn_record_id[mailbox] == 0) || mw->mbdn_not_provided == TRUE) return set_cphs_mbdn(mw, FALSE, mailbox, number, msg); if (mw->efmbdn_length == 0) { if (msg) return __ofono_error_sim_not_ready(msg); return NULL; } req = g_new0(struct mbdn_set_request, 1); req->mw = mw; req->mailbox = mailbox; string_to_phone_number(number, &req->number); req->cphs = FALSE; sim_adn_build(efmbdn, req->mw->efmbdn_length, &req->number, NULL); if (ofono_sim_write(req->mw->sim_context, SIM_EFMBDN_FILEID, mbdn_set_cb, OFONO_SIM_FILE_STRUCTURE_FIXED, req->mw->efmbdn_record_id[mailbox], efmbdn, req->mw->efmbdn_length, req) == -1) { g_free(req); if (msg) return __ofono_error_failed(msg); } else req->msg = msg ? dbus_message_ref(msg) : NULL; return NULL; } static DBusMessage *mw_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_message_waiting *mw = data; DBusMessageIter iter; DBusMessageIter var; const char *name, *value; int i; if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); for (i = 0; i < 5; i++) if (mw_mailbox_property_name[i] && !strcmp(name, mw_mailbox_property_name[i])) break; if (i < 5) { const char *cur_number; dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (!valid_phone_number_format(value)) return __ofono_error_invalid_format(msg); cur_number = phone_number_to_string(&mw->mailbox_number[i]); if (g_str_equal(cur_number, value)) return dbus_message_new_method_return(msg); return set_mbdn(mw, i, value, msg); } return __ofono_error_invalid_args(msg); } static const GDBusMethodTable message_waiting_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), mw_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, mw_set_property) }, { } }; static const GDBusSignalTable message_waiting_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static void update_indicator_and_emit(struct ofono_message_waiting *mw, int mailbox, struct mailbox_state *info) { dbus_bool_t indication; unsigned char count; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(mw->atom); if (mw->messages[mailbox].message_count == info->message_count && mw->messages[mailbox].indication == info->indication) return; memcpy(&mw->messages[mailbox], info, sizeof(struct mailbox_state)); indication = info->indication; count = info->message_count; if (mw_message_waiting_property_name[mailbox] == NULL) return; ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_message_waiting_property_name[mailbox], DBUS_TYPE_BOOLEAN, &indication); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_message_count_property_name[mailbox], DBUS_TYPE_BYTE, &count); } static void mw_cphs_mwis_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; struct mailbox_state info; unsigned char indication; if (!ok || total_length < 1) { DBG("No CPHS MWIS on SIM"); mw->ef_cphs_mwis_length = 0; return; } mw->ef_cphs_mwis_length = total_length; if (mw->efmwis_length != 0) return; /* Read Line 1 indication */ indication = data[0] & 0xf; info.indication = (indication == 0xa); info.message_count = 0; update_indicator_and_emit(mw, 0, &info); if (total_length == 1) return; /* Read Fax indication */ indication = data[1] & 0xf; info.indication = (indication == 0xa); info.message_count = 0; update_indicator_and_emit(mw, 1, &info); /* Read Data indication, map to 'Other' */ indication = (data[1] >> 4) & 0xf; info.indication = (indication == 0xa); info.message_count = 0; update_indicator_and_emit(mw, 3, &info); } static void mw_mwis_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; int i, status; struct mailbox_state info; if (!ok || record_length < 5) { ofono_error("Unable to read waiting messages numbers " "from SIM"); mw->efmwis_length = 0; return; } /* Handle only current identity (TODO: currently assumes first) */ if (record != 1) return; status = data[0]; data++; for (i = 0; i < 5 && i < record_length - 1; i++, data++) { info.indication = (status >> i) & 1; info.message_count = info.indication ? data[0] : 0; update_indicator_and_emit(mw, i, &info); } mw->efmwis_length = record_length; } static void mw_cphs_mbdn_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; int i; DBusConnection *conn = ofono_dbus_get_connection(); const char *value; if (!ok || record_length < 14 || total_length < record_length) { ofono_error("Unable to read CPHS mailbox dialling numbers " "from SIM"); mw->ef_cphs_mbdn_length = 0; mw->cphs_mbdn_not_provided = TRUE; return; } for (i = 0; i < 5; i++) if (record == mw_mailbox_to_cphs_record[i]) break; if (i == 5) return; mw->ef_cphs_mbdn_length = record_length; if (mw->mbdn_not_provided != TRUE) return; ofono_info("3GPP MBDN not provided, parsing CPHS.."); if (sim_adn_parse(data, record_length, &mw->mailbox_number[i], NULL) == FALSE) mw->mailbox_number[i].number[0] = '\0'; if (mw_mailbox_property_name[i]) { const char *path = __ofono_atom_get_path(mw->atom); value = phone_number_to_string(&mw->mailbox_number[i]); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_mailbox_property_name[i], DBUS_TYPE_STRING, &value); } } static void mw_mbdn_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; int i; DBusConnection *conn = ofono_dbus_get_connection(); const char *value; if (!ok || record_length < 14 || total_length < record_length) { ofono_error("Unable to read mailbox dialling numbers " "from SIM"); mw->efmbdn_length = 0; mw->mbdn_not_provided = TRUE; return; } for (i = 0; i < 5; i++) if (record == mw->efmbdn_record_id[i]) break; if (i == 5) return; if (sim_adn_parse(data, record_length, &mw->mailbox_number[i], NULL) == FALSE) mw->mailbox_number[i].number[0] = '\0'; if (mw_mailbox_property_name[i]) { const char *path = __ofono_atom_get_path(mw->atom); value = phone_number_to_string(&mw->mailbox_number[i]); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_mailbox_property_name[i], DBUS_TYPE_STRING, &value); } mw->efmbdn_length = record_length; } static void mw_mbdn_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; int err; mw->efmbdn_length = 0; mw->mbdn_not_provided = FALSE; err = ofono_sim_read(mw->sim_context, SIM_EFMBDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mbdn_read_cb, mw); if (err != 0) ofono_error("Unable to read EF-MBDN from SIM"); } static void mw_cphs_mbdn_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; mw->ef_cphs_mbdn_length = 0; mw->cphs_mbdn_not_provided = FALSE; ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MBDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_cphs_mbdn_read_cb, mw); } const struct ofono_phone_number *__ofono_message_waiting_get_mbdn( struct ofono_message_waiting *mw, unsigned int index) { return &mw->mailbox_number[index]; } static void mw_mbi_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; int i, err; if (!ok || record_length < 4) { ofono_error("Unable to read mailbox identifies " "from SIM"); mw->efmbdn_length = 0; mw->mbdn_not_provided = TRUE; goto out; } /* Handle only current identity (TODO: currently assumes first) */ if (record != 1) return; for (i = 0; i < 5 && i < record_length; i++) mw->efmbdn_record_id[i] = data[i]; err = ofono_sim_read(mw->sim_context, SIM_EFMBDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mbdn_read_cb, mw); mw->efmbdn_watch = ofono_sim_add_file_watch(mw->sim_context, SIM_EFMBDN_FILEID, mw_mbdn_changed, mw, NULL); if (err != 0) ofono_error("Unable to read EF-MBDN from SIM"); out: /* * Mailbox numbers located in Byte 1, bits 6 & 5, * Check for Activated & Allocated */ if (__ofono_sim_cphs_service_available(mw->sim, SIM_CPHS_SERVICE_MAILBOX_NUMBERS)) { ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MBDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_cphs_mbdn_read_cb, mw); mw->ef_cphs_mbdn_watch = ofono_sim_add_file_watch( mw->sim_context, SIM_EF_CPHS_MBDN_FILEID, mw_cphs_mbdn_changed, mw, NULL); } } static void mw_mwis_write_cb(int ok, void *userdata) { if (!ok) ofono_error("Writing new EF-MWIS failed"); } static void mw_set_indicator(struct ofono_message_waiting *mw, int profile, enum sms_mwi_type type, gboolean present, unsigned char messages) { DBusConnection *conn = ofono_dbus_get_connection(); unsigned char efmwis[255]; /* Max record size */ int i; if (mw == NULL) return; /* Handle only current identity (TODO: currently assumes first) */ if (profile != 1) return; if (mw->messages[type].indication == present && mw->messages[type].message_count == messages) return; if (mw->messages[type].indication != present) { dbus_bool_t indication; const char *path = __ofono_atom_get_path(mw->atom); indication = present; mw->messages[type].indication = present; if (mw_message_waiting_property_name[type]) ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_message_waiting_property_name[type], DBUS_TYPE_BOOLEAN, &indication); } if (mw->messages[type].message_count != messages) { const char *path = __ofono_atom_get_path(mw->atom); mw->messages[type].message_count = messages; if (mw_message_waiting_property_name[type]) ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_message_count_property_name[type], DBUS_TYPE_BYTE, &messages); } /* Writes MWI states and/or MBDN back to SIM */ if (mw->efmwis_length < 5) { if (mw->ef_cphs_mwis_length >= 1) goto try_cphs; ofono_error("Unable to update MWIS indicator"); return; } /* Fill in numbers of messages in bytes 1 to X of EF-MWIS */ for (i = 0; i < 5 && i < mw->efmwis_length - 1; i++) efmwis[i + 1] = mw->messages[i].message_count; /* Fill in indicator state bits in byte 0 */ efmwis[0] = 0; for (i = 0; i < 5 && i < mw->efmwis_length - 1; i++) if (mw->messages[i].indication) efmwis[0] |= 1 << i; if (ofono_sim_write(mw->sim_context, SIM_EFMWIS_FILEID, mw_mwis_write_cb, OFONO_SIM_FILE_STRUCTURE_FIXED, 1, efmwis, mw->efmwis_length, mw) != 0) { ofono_error("Queuing a EF-MWI write to SIM failed"); } if (mw->ef_cphs_mwis_length == 0) return; try_cphs: memset(efmwis, 0x55, 255); efmwis[0] = mw->messages[0].indication ? 0xa : 0x5; if (mw->ef_cphs_mwis_length > 1) efmwis[1] = mw->messages[1].indication ? 0xa : 0x5 | mw->messages[3].indication ? 0xa0 : 0x50; if (ofono_sim_write(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID, mw_mwis_write_cb, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, 0, efmwis, mw->ef_cphs_mwis_length, mw) != 0) ofono_error("Queuing a EF-MWIS write to SIM failed (CPHS)"); } static void handle_special_sms_iei(struct ofono_message_waiting *mw, const guint8 *iei, gboolean *discard) { enum sms_mwi_type type; int profile; gboolean set; /* Parse type & storage byte */ if (discard) *discard = (iei[0] & (1 << 7)) ? FALSE : TRUE; type = iei[0] & 0x1f; if (type > SMS_MWI_TYPE_OTHER) { if (type == (SMS_MWI_TYPE_OTHER | 4)) type = SMS_MWI_TYPE_VIDEO; else /* * 23.040 9.2.3.24.2: "Terminals should be capable of * receiving any values in octet 1, even including * those marked as Reserved." Treat Reserved as * "Other". */ type = SMS_MWI_TYPE_OTHER; } set = iei[1] > 0 ? TRUE : FALSE; profile = ((iei[0] >> 5) & 3) + 1; mw_set_indicator(mw, profile, type, set, iei[1]); } static void handle_enhanced_voicemail_iei(struct ofono_message_waiting *mw, const guint8 *iei, gboolean *discard, int length) { int profile, n; gboolean set; struct sms_address mailbox_address; if (length < 3) return; /* ENHANCED_VOICE_MAIL_PDU_TYPE */ if (!(iei[0] & 1)) { /* 9.2.3.24.13.1 Enhanced Voice Mail Notification */ /* MULTIPLE_SUBSCRIBER_PROFILE */ profile = ((iei[0] >> 2) & 3) + 1; /* SM_STORAGE */ if (discard) *discard = (iei[0] & (1 << 4)) ? FALSE : TRUE; /* VM_MAILBOX_ACCESS_ADDRESS */ n = 0; if (!sms_decode_address_field(iei + 1, length - 1, &n, FALSE, &mailbox_address)) return; /* TODO: VM_MESSAGE_PRIORITY_INDICATION */ /* Other parameters currently not supported */ if (length < n + 3) return; set = iei[n + 1] > 0 ? TRUE : FALSE; mw_set_indicator(mw, profile, SMS_MWI_TYPE_VOICE, set, iei[n + 1]); } else { /* 9.2.3.24.13.2 Enhanced Voice Delete Confirmation */ /* MULTIPLE_SUBSCRIBER_PROFILE */ profile = ((iei[0] >> 2) & 3) + 1; /* SM_STORAGE */ if (discard) *discard = (iei[0] & (1 << 4)) ? FALSE : TRUE; /* VM_MAILBOX_ACCESS_ADDRESS */ n = 0; if (!sms_decode_address_field(iei + 1, length - 1, &n, FALSE, &mailbox_address)) return; /* Other parameters currently not supported */ if (length < n + 3) return; set = iei[n + 1] > 0 ? TRUE : FALSE; mw_set_indicator(mw, profile, SMS_MWI_TYPE_VOICE, set, iei[n + 1]); } if (mailbox_address.address[0] != '\0') set_mbdn(mw, SMS_MWI_TYPE_VOICE, sms_address_to_string(&mailbox_address), NULL); } void __ofono_message_waiting_mwi(struct ofono_message_waiting *mw, struct sms *sms, gboolean *out_discard) { gboolean active, discard; enum sms_mwi_type type; int profile = 1, iei_found = 0; if (out_discard) *out_discard = FALSE; /* * Check MWI types in the order from highest priority to lowest * because they must override one another. */ if (sms->deliver.udhi) { guint8 evm_iei[140]; struct sms_udh_iter iter; enum sms_iei iei; if (!sms_udh_iter_init(sms, &iter)) return; while ((iei = sms_udh_iter_get_ie_type(&iter)) != SMS_IEI_INVALID) { switch (iei) { case SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION: sms_udh_iter_get_ie_data(&iter, evm_iei); handle_enhanced_voicemail_iei(mw, evm_iei, out_discard, sms_udh_iter_get_ie_length( &iter)); return; default: break; } sms_udh_iter_next(&iter); } } if (sms->deliver.udhi) { guint8 special_iei[4]; struct sms_udh_iter iter; enum sms_iei iei; if (!sms_udh_iter_init(sms, &iter)) return; while ((iei = sms_udh_iter_get_ie_type(&iter)) != SMS_IEI_INVALID) { switch (iei) { case SMS_IEI_SPECIAL_MESSAGE_INDICATION: if (sms_udh_iter_get_ie_length(&iter) != 2) break; sms_udh_iter_get_ie_data(&iter, special_iei); handle_special_sms_iei(mw, special_iei, &discard); if (out_discard) *out_discard = *out_discard || discard; iei_found = 1; break; default: break; } sms_udh_iter_next(&iter); } if (iei_found) { /* * 23.040 9.2.3.24.2 says "In the event of a * conflict between this setting and the setting * of the Data Coding Scheme (see 3GPP TS 23.038 [9]) * then the message shall be stored if either the DCS * indicates this, or Octet 1 above indicates this." */ if (sms_mwi_dcs_decode(sms->deliver.dcs, NULL, NULL, NULL, &discard)) { if (out_discard) *out_discard = *out_discard || discard; } return; } } if (sms_mwi_dcs_decode(sms->deliver.dcs, &type, NULL, &active, out_discard)) { mw_set_indicator(mw, profile, type, active, 0); return; } if (sms->deliver.pid == SMS_PID_TYPE_RETURN_CALL) return; } static void message_waiting_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); struct ofono_message_waiting *mw = __ofono_atom_get_data(atom); if (mw->sim_context) { ofono_sim_context_free(mw->sim_context); mw->sim_context = NULL; } mw->sim = NULL; g_dbus_unregister_interface(conn, path, OFONO_MESSAGE_WAITING_INTERFACE); ofono_modem_remove_interface(modem, OFONO_MESSAGE_WAITING_INTERFACE); } static void mw_mwis_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; mw->efmwis_length = 0; ofono_sim_read(mw->sim_context, SIM_EFMWIS_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mwis_read_cb, mw); } static void mw_cphs_mwis_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; mw->ef_cphs_mwis_length = 0; ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, mw_cphs_mwis_read_cb, mw); } static void mw_mbi_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; mw->efmbdn_length = 0; mw->mbdn_not_provided = FALSE; mw->ef_cphs_mbdn_length = 0; mw->cphs_mbdn_not_provided = FALSE; ofono_sim_remove_file_watch(mw->sim_context, mw->efmbdn_watch); ofono_sim_remove_file_watch(mw->sim_context, mw->ef_cphs_mbdn_watch); ofono_sim_read(mw->sim_context, SIM_EFMBI_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mbi_read_cb, mw); } void ofono_message_waiting_register(struct ofono_message_waiting *mw) { DBusConnection *conn; const char *path; struct ofono_modem *modem; if (mw == NULL) return; conn = ofono_dbus_get_connection(); modem = __ofono_atom_get_modem(mw->atom); path = __ofono_atom_get_path(mw->atom); if (!g_dbus_register_interface(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, message_waiting_methods, message_waiting_signals, NULL, mw, NULL)) { ofono_error("Could not create %s interface", OFONO_MESSAGE_WAITING_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_MESSAGE_WAITING_INTERFACE); mw->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); if (mw->sim) { /* Assume that if sim atom exists, it is ready */ mw->sim_context = ofono_sim_context_create(mw->sim); /* Loads MWI states and MBDN from SIM */ ofono_sim_read(mw->sim_context, SIM_EFMWIS_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mwis_read_cb, mw); ofono_sim_read(mw->sim_context, SIM_EFMBI_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mbi_read_cb, mw); /* Also read CPHS MWIS field */ ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, mw_cphs_mwis_read_cb, mw); /* * The operator could send us SMS mwi updates, but let's be * extra careful and track the file contents too. */ ofono_sim_add_file_watch(mw->sim_context, SIM_EFMWIS_FILEID, mw_mwis_changed, mw, NULL); ofono_sim_add_file_watch(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID, mw_cphs_mwis_changed, mw, NULL); ofono_sim_add_file_watch(mw->sim_context, SIM_EFMBI_FILEID, mw_mbi_changed, mw, NULL); } __ofono_atom_register(mw->atom, message_waiting_unregister); } static void mw_remove(struct ofono_atom *atom) { struct ofono_message_waiting *mw = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (mw == NULL) return; g_free(mw); } struct ofono_message_waiting *ofono_message_waiting_create(struct ofono_modem *modem) { struct ofono_message_waiting *mw; mw = g_try_new0(struct ofono_message_waiting, 1); if (mw == NULL) return NULL; mw->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_MESSAGE_WAITING, mw_remove, mw); return mw; } void ofono_message_waiting_remove(struct ofono_message_waiting *mw) { __ofono_atom_free(mw->atom); }