2009/7/31 Denis Kenzior <[email protected]>:
>> +     if (mw->messages[i] != value) {
>> +             mw->messages[i] = value;
>> +
>> +             if (!mw->pending)
>> +                     mw->pending = g_timeout_add(0, mw_mwis_update, modem);
>> +
>> +             dbus_gsm_signal_property_changed(conn, modem->path,
>> +                             MESSAGE_WAITING_INTERFACE,
>> +                             mw_messages_property_name[i],
>> +                             DBUS_TYPE_BYTE, &value);
>> +     }
>> +
>> +     return dbus_message_new_method_return(msg);
>> +}
>
> Ok, I'm a bit confused here.  Are you sure that MWIs should have the ability
> to be cleared out by the user?  The way I understood it, the user dials the
> voice mail system and the provider sends another MWI message which clears out
> the status.

You're right, there's probably no point to provide this method.  I
haven't found the exact statement in the docs saying that the provider
should send a new MWI but it's logical.  Otherwise I thought the D-Bus
client might be able to tell somehow that the mailbox should now have
1 message less and want to update us.

>
>> +
>> +enum mwi_information_type {
>> +     MWI_UNSPECIFIED = -1,
>> +     MWI_MESSAGES_WAITING = -2,
>> +     MWI_NO_MESSAGES_WAITING = -3,
>> +};
>
> Is there a reason these are negative?

So the caller can give the exact number of messages (zero or positive)
or partial information using one of the constants.

>
>> +static void handle_mwi(struct ofono_modem *modem,
>> +                     struct sms *sms, gboolean *out_discard)
>> +{
>> +     gboolean active, discard;
>> +     enum sms_mwi_type type;
>> +     char *message;
>> +     int profile = 1;
>> +     GSList *sms_list;
>> +
>> +     /* "Store" bits are ORed if multiple MWI types are present
>> +      * but if neither Special SMS Message Indication nor DCS based
>> +      * indication is present, the bit must remain set.  */
>> +     int set = 1, store = 0;
>
> What is this comment about, since I don't see any ORing going on below.  Are
> you trying to accommodate this piece of stupidity from 23.040 9.2.3.24.2?
> "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."

Yes. :-)

I'm thinking it's better to try to implement what's in the text so
when someone needs to know how ofono will react to something, you can
point at the specification, barring any TODOs in the code.

>
>> +
>> +     /* Check MWI types in the order from lowest to highest priority
>> +      * because they will override one another.  */
>
> Can we instead process the different sources from highest to lowest instead 
> and
> bail out early?  No sense in calling message_waiting_notify several times (and
> possibly emitting spurious signals) when only once is required.

We can but then we won't comply with that 23.040 9.2.3.24.2 above.
There's also a tiny possibility someone might send us updates for the
different mailboxes (out of the 5 types defined) one using DCS and
another one in UDH.  In the attached patch I left this as is and made
sure PropertyChanged is only sent from the time callback (bottom half)
which was my original intention.  I can still change this.

>
>> +     if (sms_dcs_decode(sms->deliver.dcs, NULL, NULL, NULL, NULL) &&
>> +                     sms->deliver.udhi) {
>
> This seems to be incorrect.  An MWI DCS message can still contain enhanced
> IEIs.  Should this check just for UDHI instead?

True, I had wrongly copied the condition from ofono_sms_deliver_notify.

>
>> +final:
>> +     /* Only bother the Message Waiting interface with textual
>> +      * notifications that are not to be stored together with other
>> +      * Short Messages in ME.  The other messages will be delivered
>> +      * to the user through normal SMS store.  */
>
> It sounds to me like we can completely ignore the text of such messages.  The
> network is basically saying: "the text isn't important, the contents are."

Probably, although with the Return Call type of notification there's
no other information beside the text.  I left it in the updated patch.

Regards
From 68f9bb3807a003f873ca3f33575013291b094e89 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <[email protected]>
Date: Sat, 1 Aug 2009 15:40:58 +0200
Subject: [PATCH] Add a MessageWaiting interface for tracking message waiting indications.

The state of the indications is kept in memory and written back to
the SIM after any changes.
---
 src/Makefile.am       |    3 +-
 src/message-waiting.c |  467 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/message-waiting.h |   33 ++++
 src/ofono.h           |    4 +
 src/simutil.h         |    3 +
 src/sms.c             |  218 ++++++++++++++++++++++-
 src/smsutil.h         |    1 +
 7 files changed, 722 insertions(+), 7 deletions(-)
 create mode 100644 src/message-waiting.c
 create mode 100644 src/message-waiting.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 2e3e607..4644c69 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,7 +13,8 @@ ofonod_SOURCES = main.c ofono.h log.c plugin.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 \
-		phonebook.c history.c simutil.h simutil.c
+		phonebook.c history.c simutil.h simutil.c \
+		message-waiting.c message-waiting.h
 
 ofonod_LDADD = $(top_builddir)/plugins/libbuiltin.la \
 		$(top_builddir)/drivers/libbuiltin.la \
diff --git a/src/message-waiting.c b/src/message-waiting.c
new file mode 100644
index 0000000..0e5c80b
--- /dev/null
+++ b/src/message-waiting.c
@@ -0,0 +1,467 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  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 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 "driver.h"
+#include "common.h"
+#include "util.h"
+#include "sim.h"
+#include "simutil.h"
+#include "smsutil.h"
+#include "message-waiting.h"
+
+#define MESSAGE_WAITING_INTERFACE "org.ofono.MessageWaiting"
+
+struct message_waiting_data {
+	int pending;
+
+	int messages[5];
+	int efmwis_length;
+	int efmbdn_length;
+
+	struct ofono_phone_number mailbox_number[5];
+
+	char *last_message;
+	int messages_new[5];
+};
+
+static struct message_waiting_data *message_waiting_create()
+{
+	return g_try_new0(struct message_waiting_data, 1);
+}
+
+static void message_waiting_destroy(gpointer userdata)
+{
+	struct ofono_modem *modem = userdata;
+	struct message_waiting_data *data = modem->message_waiting;
+
+	if (data->last_message)
+		g_free(data->last_message);
+
+	g_free(data);
+
+	modem->message_waiting = NULL;
+}
+
+static const char *mw_messages_property_name[5] = {
+	"VoicemailMessages",
+#if 0
+	"FaxMessages",
+	"EmailMessages",
+	"OtherMessages",
+	"VideomailMessages",
+#endif
+};
+
+static const char *mw_mailbox_property_name[5] = {
+	"VoicemailMailboxNumber",
+#if 0
+	"FaxMailboxNumber",
+	"EmailMailboxNumber",
+	"OtherMailboxNumber",
+	"VideomailMailboxNumber",
+#endif
+};
+
+static DBusMessage *mw_get_properties(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct ofono_modem *modem = data;
+	struct message_waiting_data *mw = modem->message_waiting;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	int i;
+	unsigned char value;
+	const char *number;
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict);
+
+	for (i = 0; i < 5; i++)
+		if (mw_messages_property_name[i]) {
+			value = mw->messages[i];
+
+			ofono_dbus_dict_append(&dict,
+					mw_messages_property_name[i],
+					DBUS_TYPE_BYTE, &value);
+		}
+
+	for (i = 0; i < 5; i++)
+		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);
+		}
+
+	ofono_dbus_dict_append(&dict, "LastNotificationText",
+			DBUS_TYPE_STRING, &mw->last_message);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static GDBusMethodTable message_waiting_methods[] = {
+	{ "GetProperties",	"",	"a{sv}",	mw_get_properties	},
+	{ }
+};
+
+static GDBusSignalTable message_waiting_signals[] = {
+	{ "PropertyChanged",	"sv" },
+	{ }
+};
+
+static void mw_mwis_read_cb(struct ofono_modem *modem, int ok,
+		enum ofono_sim_file_structure structure, int total_length,
+		int record, const unsigned char *data, int record_length,
+		void *userdata)
+{
+	int i, status, messages[5];
+	unsigned char value;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	struct message_waiting_data *mw = modem->message_waiting;
+
+	/* Handle only current identity (TODO: currently assumes first) */
+	if (record != 1)
+		return;
+
+	if (!ok ||
+		structure != OFONO_SIM_FILE_STRUCTURE_FIXED ||
+		record_length < 5) {
+		ofono_error("Unable to read waiting messages numbers "
+			"from SIM");
+
+		mw->efmwis_length = -1;
+
+		return;
+	}
+
+	status = data[0];
+	data++;
+
+	for (i = 0; i < 5 && i < record_length - 1; i++) {
+		messages[i] = ((status >> i) & 1) ? data[i] : 0;
+
+		if (mw->messages[i] != messages[i]) {
+			mw->messages[i] = messages[i];
+			value = messages[i];
+
+			if (!mw_messages_property_name[i])
+				continue;
+
+			ofono_dbus_signal_property_changed(conn, modem->path,
+					MESSAGE_WAITING_INTERFACE,
+					mw_messages_property_name[i],
+					DBUS_TYPE_BYTE, &value);
+		}
+	}
+
+	mw->efmwis_length = record_length;
+}
+
+static void mw_mbdn_read_cb(struct ofono_modem *modem, int ok,
+		enum ofono_sim_file_structure structure, int total_length,
+		int record, const unsigned char *data, int record_length,
+		void *userdata)
+{
+	int i, *ids = userdata;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	struct message_waiting_data *mw = modem->message_waiting;
+
+	if (!ok ||
+		structure != OFONO_SIM_FILE_STRUCTURE_FIXED ||
+		record_length < 14 || total_length < record_length) {
+		ofono_error("Unable to read mailbox dialling numbers "
+			"from SIM");
+		g_free(ids);
+		return;
+	}
+
+	for (i = 0; i < 5; i++)
+		if (record == ids[i])
+			break;
+	if (i == 5)
+		goto final;
+
+	if (sim_adn_parse(data, record_length, &mw->mailbox_number[i]) ==
+			FALSE)
+		mw->mailbox_number[i].number[0] = '\0';
+
+	mw->efmbdn_length = record_length;
+
+final:
+	/* Is this the last MBDN record? */
+	if (record == total_length / record_length)
+		g_free(ids);
+}
+
+static void mw_mbi_read_cb(struct ofono_modem *modem, int ok,
+		enum ofono_sim_file_structure structure, int total_length,
+		int record, const unsigned char *data, int record_length,
+		void *userdata)
+{
+	int i, err, *ids = userdata;
+	struct message_waiting_data *mw = modem->message_waiting;
+
+	/* Handle only current identity (TODO: currently assumes first) */
+	if (record != 1)
+		return;
+
+	if (!ok ||
+		structure != OFONO_SIM_FILE_STRUCTURE_FIXED ||
+		record_length < 4) {
+		ofono_error("Unable to read mailbox identifies "
+			"from SIM");
+
+		g_free(ids);
+		return;
+	}
+
+	for (i = 0; i < 5 && i < record_length; i++)
+		ids[i] = data[i];
+
+	err = ofono_sim_read(modem, SIM_EFMBDN_FILEID, mw_mbdn_read_cb, ids);
+	if (err != 0) {
+		ofono_error("Unable to read EF-MBDN from SIM");
+
+		g_free(ids);
+	}
+}
+
+static void mw_mbdn_write_cb(struct ofono_modem *modem, int ok, void *userdata)
+{
+	if (!ok)
+		ofono_error("Writing new EF-MBDN failed");
+}
+
+/* Loads MWI states from SIM */
+static gboolean mw_mwis_load(struct ofono_modem *modem)
+{
+	int err;
+	int *ids;
+
+	err = ofono_sim_read(modem, SIM_EFMWIS_FILEID, mw_mwis_read_cb, NULL);
+	if (err != 0)
+		return FALSE;
+
+	/* Alloc space for mailbox identifiers */
+	ids = g_malloc0(5);
+
+	err = ofono_sim_read(modem, SIM_EFMBI_FILEID, mw_mbi_read_cb, ids);
+	if (err != 0) {
+		g_free(ids);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/* Writes MWI states back to SIM */
+static gboolean mw_mwis_update(gpointer user)
+{
+	struct ofono_modem *modem = user;
+	struct message_waiting_data *mw = modem->message_waiting;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	unsigned char value;
+	unsigned char *file;
+	int i;
+
+	for (i = 0; i < 5; i++)
+		if (mw->messages[i] != mw->messages_new[i]) {
+			mw->messages[i] = mw->messages_new[i];
+
+			if (!mw_messages_property_name[i])
+				continue;
+
+			value = mw->messages[i];
+
+			ofono_dbus_signal_property_changed(conn, modem->path,
+					MESSAGE_WAITING_INTERFACE,
+					mw_messages_property_name[i],
+					DBUS_TYPE_BYTE, &value);
+		}
+
+	mw->pending = 0;
+
+	if (mw->efmwis_length < 1)
+		return FALSE;
+
+	file = g_malloc0(mw->efmwis_length);
+
+	/* Fill in numbers of messages in bytes 1 to X of EF-MWIS */
+	for (i = 0; i < 5 && i < mw->efmwis_length - 1; i++)
+		file[i + 1] = mw->messages[i];
+
+	/* Fill in indicator state bits in byte 0 */
+	for (i = 0; i < 5 && i < mw->efmwis_length - 1; i++)
+		if (file[i + 1] > 0)
+			file[0] |= 1 << i;
+
+	if (ofono_sim_write(modem, SIM_EFMWIS_FILEID, mw_mbdn_write_cb,
+				OFONO_SIM_FILE_STRUCTURE_FIXED, 1,
+				file, mw->efmwis_length, NULL) != 0) {
+		ofono_error("Queuing a EF-MWI write to SIM failed");
+	}
+
+	g_free(file);
+
+	return FALSE;
+}
+
+void ofono_message_waiting_notify(struct ofono_modem *modem, int information,
+					enum sms_mwi_type type, int profile)
+{
+	struct message_waiting_data *mw = modem->message_waiting;
+	int invalidate;
+
+	if (mw == NULL)
+		return;
+
+	/* Handle only current identity (TODO: currently assumes first) */
+	if (profile != 1)
+		return;
+
+	/* Ignore the unspecified type of notification.  It probably means
+	 * that the (voicemail) indication should become active but there's
+	 * no way to know when to deactivate it.  It's used for the obsolete
+	 * Return Call type of indication in SMS PDUs which also carry text
+	 * and we provide this text instead.  */
+	if (information == MWI_UNSPECIFIED)
+		return;
+
+	memcpy(mw->messages_new, mw->messages, sizeof(mw->messages_new));
+
+	if (information == MWI_MESSAGES_WAITING) {
+		if (mw->messages[type] == 0) {
+			mw->messages_new[type] = 1;
+			invalidate = 1;
+		}
+	} else if (information == MWI_NO_MESSAGES_WAITING) {
+		if (mw->messages[type] > 0) {
+			mw->messages_new[type] = 0;
+			invalidate = 1;
+		}
+	} else {
+		if (mw->messages[type] != information) {
+			mw->messages_new[type] = information;
+			invalidate = 1;
+		}
+	}
+
+	if (invalidate && !mw->pending)
+		mw->pending = g_timeout_add(0, mw_mwis_update, modem);
+}
+
+void ofono_message_waiting_message(struct ofono_modem *modem, const char *msg)
+{
+	struct message_waiting_data *mw = modem->message_waiting;
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	if (!mw)
+		return;
+
+	if (mw->last_message && !strcmp(mw->last_message, msg))
+		return;
+
+	if (mw->last_message)
+		g_free(mw->last_message);
+
+	mw->last_message = g_strdup(msg);
+
+	ofono_dbus_signal_property_changed(conn, modem->path,
+			MESSAGE_WAITING_INTERFACE, "LastNotificationText",
+			DBUS_TYPE_STRING, &mw->last_message);
+}
+
+static void initialize_message_waiting(struct ofono_modem *modem)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	if (!mw_mwis_load(modem)) {
+		ofono_error("Could not register MessageWaiting interface");
+		message_waiting_destroy(modem);
+
+		return;
+	}
+
+	if (!g_dbus_register_interface(conn, modem->path,
+					MESSAGE_WAITING_INTERFACE,
+					message_waiting_methods,
+					message_waiting_signals,
+					NULL, modem,
+					message_waiting_destroy)) {
+		ofono_error("Could not register MessageWaiting interface");
+		message_waiting_destroy(modem);
+
+		return;
+	}
+
+	ofono_debug("MessageWaiting interface for modem: %s created",
+			modem->path);
+
+	ofono_modem_add_interface(modem, MESSAGE_WAITING_INTERFACE);
+}
+
+int ofono_message_waiting_register(struct ofono_modem *modem)
+{
+	if (modem == NULL)
+		return -1;
+
+	modem->message_waiting = message_waiting_create();
+
+	ofono_sim_ready_notify_register(modem, initialize_message_waiting);
+	if (ofono_sim_get_ready(modem))
+		initialize_message_waiting(modem);
+
+	return 0;
+}
+
+void ofono_message_waiting_unregister(struct ofono_modem *modem)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	g_dbus_unregister_interface(conn, modem->path,
+					MESSAGE_WAITING_INTERFACE);
+	ofono_modem_remove_interface(modem, MESSAGE_WAITING_INTERFACE);
+}
diff --git a/src/message-waiting.h b/src/message-waiting.h
new file mode 100644
index 0000000..cad4c40
--- /dev/null
+++ b/src/message-waiting.h
@@ -0,0 +1,33 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  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 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
+ *
+ */
+
+enum mwi_information_type {
+	MWI_UNSPECIFIED = -1,
+	MWI_MESSAGES_WAITING = -2,
+	MWI_NO_MESSAGES_WAITING = -3,
+};
+
+/* information either a positive (or zero) message count or a
+ * partial information using one of mwi_information_type constants
+ */
+void ofono_message_waiting_notify(struct ofono_modem *modem, int information,
+					enum sms_mwi_type type, int profile);
+void ofono_message_waiting_message(struct ofono_modem *modem, const char *msg);
diff --git a/src/ofono.h b/src/ofono.h
index 3cb2d75..4e34430 100644
--- a/src/ofono.h
+++ b/src/ofono.h
@@ -79,6 +79,7 @@ struct ofono_modem {
 	struct sim_manager_data *sim_manager;
 	struct sms_manager_data *sms_manager;
 	struct phonebook_data *phonebook;
+	struct message_waiting_data *message_waiting;
 
 	GSList *history_contexts;
 };
@@ -99,3 +100,6 @@ void __ofono_history_call_ended(struct ofono_modem *modem,
 
 void __ofono_history_call_missed(struct ofono_modem *modem,
 				const struct ofono_call *call, time_t when);
+
+int ofono_message_waiting_register(struct ofono_modem *modem);
+void ofono_message_waiting_unregister(struct ofono_modem *modem);
diff --git a/src/simutil.h b/src/simutil.h
index 6572e72..c2b1e20 100644
--- a/src/simutil.h
+++ b/src/simutil.h
@@ -24,6 +24,9 @@ enum sim_fileid {
 	SIM_EFSPN_FILEID = 0x6f46,
 	SIM_EFPNN_FILEID = 0x6fc5,
 	SIM_EFOPL_FILEID = 0x6fc6,
+	SIM_EFMBDN_FILEID = 0x6fc7,
+	SIM_EFMBI_FILEID = 0x6fc9,
+	SIM_EFMWIS_FILEID = 0x6fca,
 	SIM_EFSPDI_FILEID = 0x6fcd,
 };
 
diff --git a/src/sms.c b/src/sms.c
index 3a471e3..e844e41 100644
--- a/src/sms.c
+++ b/src/sms.c
@@ -36,6 +36,7 @@
 #include "util.h"
 #include "sim.h"
 #include "smsutil.h"
+#include "message-waiting.h"
 
 #define uninitialized_var(x) x = x
 
@@ -655,9 +656,198 @@ static void handle_deliver(struct ofono_modem *modem,
 	g_slist_free(l);
 }
 
-static void handle_mwi(struct ofono_modem *modem, struct sms *mwi)
+static void handle_special_sms_iei(struct ofono_modem *modem,
+		const guint8 *iei, int *set, int *store)
 {
-	ofono_error("MWI information not yet handled");
+	enum sms_mwi_type type;
+	int profile;
+
+	/* Parse type & storage byte */
+	*set = 0;
+	if (iei[0] & (1 << 7))
+		*store = 1;
+
+	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;
+	}
+
+	profile = ((iei[0] >> 5) & 3) + 1;
+
+	ofono_message_waiting_notify(modem, iei[1], type, profile);
+}
+
+static void handle_enhanced_voicemail_iei(struct ofono_modem *modem,
+		const guint8 *iei, int *set, int *store, int length)
+{
+	int profile, n;
+
+	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;
+
+		/* SM_STORAGE */
+		*set = 0;
+		if (iei[0] & (1 << 4))
+			*store = 1;
+
+		/* TODO: VM_MAILBOX_ACCESS_ADDRESS */
+		n = 2 + (iei[1] + 1) / 2;
+		if (length < n + 11)
+			return;
+
+		/* TODO: VM_MESSAGE_PRIORITY_INDICATION */
+
+		/* Other parameters currently not supported */
+
+		ofono_message_waiting_notify(modem, iei[2 + n],
+				SMS_MWI_TYPE_VOICE, profile);
+	} else {
+		/* 9.2.3.24.13.2 Enhanced Voice Delete Confirmation */
+
+		/* MULTIPLE_SUBSCRIBER_PROFILE */
+		profile = (iei[0] >> 2) & 3;
+
+		/* SM_STORAGE */
+		*set = 0;
+		if (iei[0] & (1 << 4))
+			*store = 1;
+
+		/* TODO: VM_MAILBOX_ACCESS_ADDRESS */
+		n = 2 + (iei[1] + 1) / 2;
+		if (length < n + 11)
+			return;
+
+		/* Other parameters currently not supported */
+
+		ofono_message_waiting_notify(modem, iei[2 + n],
+				SMS_MWI_TYPE_VOICE, profile);
+	}
+}
+
+static void handle_mwi(struct ofono_modem *modem,
+			struct sms *sms, gboolean *out_discard)
+{
+	gboolean active, discard;
+	enum sms_mwi_type type;
+	char *message;
+	int profile = 1;
+	GSList *sms_list;
+
+	/* 23.040 9.2.3.24.2 implies "Store" bits are ORed if multiple
+	 * MWI types are present but if neither Special SMS Message
+	 * Indication nor DCS based indication is present, the bit
+	 * must remain set.
+	 */
+	int set = 1, store = 0;
+
+	/* Check MWI types in the order from lowest to highest priority
+	 * because they will override one another.
+	 */
+
+	if (sms->deliver.pid == SMS_PID_TYPE_RETURN_CALL) {
+		ofono_message_waiting_notify(modem, MWI_UNSPECIFIED,
+				SMS_MWI_TYPE_VOICE, profile);
+	}
+
+	if (sms_mwi_dcs_decode(sms->deliver.dcs, &type,
+				NULL, &active, &discard)) {
+		set = 0;
+		store = (discard == FALSE);
+
+		if (active)
+			ofono_message_waiting_notify(modem,
+				MWI_MESSAGES_WAITING, type, profile);
+		else
+			ofono_message_waiting_notify(modem,
+				MWI_NO_MESSAGES_WAITING, type, profile);
+	}
+
+	if (sms->deliver.udhi) {
+		guint8 special_iei[4], *evm_iei;
+		struct sms_udh_iter iter;
+		enum sms_iei iei;
+
+		if (!sms_udh_iter_init(sms, &iter))
+			goto final;
+
+		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(modem, special_iei,
+						&set, &store);
+				break;
+			}
+
+			sms_udh_iter_next(&iter);
+		}
+
+		/* Go through the UDH again because Enhanced Voice Mail
+		 * Notification takes precedence over both DCS based
+		 * indication and Special SMS Message indication
+		 * (23.040 9.2.3.24.13.1).  */
+
+		if (!sms_udh_iter_init(sms, &iter))
+			goto final;
+
+		while ((iei = sms_udh_iter_get_ie_type(&iter)) !=
+				SMS_IEI_INVALID) {
+			switch (iei) {
+			case SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION:
+				evm_iei = g_malloc0(
+						sms_udh_iter_get_ie_length(
+							&iter));
+				sms_udh_iter_get_ie_data(&iter, evm_iei);
+
+				handle_enhanced_voicemail_iei(modem, evm_iei,
+						&set, &store,
+						sms_udh_iter_get_ie_length(
+							&iter));
+
+				g_free(evm_iei);
+				break;
+			}
+
+			sms_udh_iter_next(&iter);
+		}
+	}
+
+final:
+	/* Only bother the Message Waiting interface with textual
+	 * notifications that are not to be stored together with other
+	 * Short Messages in ME.  The other messages will be delivered
+	 * to the user through normal SMS store.  */
+	if (!store) {
+		sms_list = g_slist_append(NULL, sms);
+		message = sms_decode_text(sms_list);
+		g_slist_free(sms_list);
+
+		if (message) {
+			ofono_message_waiting_message(modem, message);
+			g_free(message);
+		}
+	}
+
+	if (out_discard)
+		*out_discard = !(set || store);
 }
 
 void ofono_sms_deliver_notify(struct ofono_modem *modem, unsigned char *pdu,
@@ -685,14 +875,18 @@ void ofono_sms_deliver_notify(struct ofono_modem *modem, unsigned char *pdu,
 	/* This is an older style MWI notification, process MWI
 	 * headers and handle it like any other message */
 	if (sms.deliver.pid == SMS_PID_TYPE_RETURN_CALL) {
-		handle_mwi(modem, &sms);
+		handle_mwi(modem, &sms, &discard);
+
+		if (discard)
+			return;
+
 		goto out;
 	}
 
 	/* The DCS indicates this is an MWI notification, process it
 	 * and then handle the User-Data as any other message */
-	if (sms_mwi_dcs_decode(sms.deliver.dcs, NULL, NULL, NULL, &discard)) {
-		handle_mwi(modem, &sms);
+	if (sms_mwi_dcs_decode(sms.deliver.dcs, NULL, NULL, NULL, NULL)) {
+		handle_mwi(modem, &sms, &discard);
 
 		if (discard)
 			return;
@@ -757,7 +951,14 @@ void ofono_sms_deliver_notify(struct ofono_modem *modem, unsigned char *pdu,
 			switch (iei) {
 			case SMS_IEI_SPECIAL_MESSAGE_INDICATION:
 			case SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION:
-				handle_mwi(modem, &sms);
+				/* TODO: ignore if not in the very first
+				 * segment of a concatenated SM so as not
+				 * to repeat the indication.  */
+				handle_mwi(modem, &sms, &discard);
+
+				if (discard)
+					return;
+
 				goto out;
 			case SMS_IEI_WCMP:
 				ofono_error("No support for WCMP, ignoring");
@@ -789,6 +990,9 @@ int ofono_sms_manager_register(struct ofono_modem *modem,
 	if (ops == NULL)
 		return -1;
 
+	if (ofono_message_waiting_register(modem))
+		return -1;
+
 	modem->sms_manager = sms_manager_create();
 
 	if (!modem->sms_manager)
@@ -824,4 +1028,6 @@ void ofono_sms_manager_unregister(struct ofono_modem *modem)
 					SMS_MANAGER_INTERFACE);
 
 	ofono_modem_remove_interface(modem, SMS_MANAGER_INTERFACE);
+
+	ofono_message_waiting_unregister(modem);
 }
diff --git a/src/smsutil.h b/src/smsutil.h
index 89da973..d362aa9 100644
--- a/src/smsutil.h
+++ b/src/smsutil.h
@@ -158,6 +158,7 @@ enum sms_mwi_type {
 	SMS_MWI_TYPE_FAX = 1,
 	SMS_MWI_TYPE_EMAIL = 2,
 	SMS_MWI_TYPE_OTHER = 3,
+	SMS_MWI_TYPE_VIDEO = 4,
 };
 
 enum sms_pid_type {
-- 
1.6.1

_______________________________________________
ofono mailing list
[email protected]
http://lists.ofono.org/listinfo/ofono

Reply via email to