ofono/src/message-waiting.c

1117 lines
27 KiB
C

/*
*
* 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 <config.h>
#endif
#include <string.h>
#include <stdio.h>
#include <dbus/dbus.h>
#include <glib.h>
#include <gdbus.h>
#include <sys/stat.h>
#include <sys/types.h>
#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);
}