Files
ModemManager/src/mm-iface-modem-messaging.c
T
Aleksander Morgado a2705abb24 iface-modem-messaging: if only one storage supported, select it right away
Some plugins or implementations (e.g. notably MBIM) may report a
single storage as supported and no way to update the current default
storage. In this specific case, we will initialize the default storage
to that single one supported right away, regardless of whether
selecting others is implemented or not.
2018-09-25 17:51:38 +00:00

1500 lines
48 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2012 Google, Inc.
*/
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-iface-modem.h"
#include "mm-iface-modem-messaging.h"
#include "mm-sms-list.h"
#include "mm-log.h"
#define SUPPORT_CHECKED_TAG "messaging-support-checked-tag"
#define SUPPORTED_TAG "messaging-supported-tag"
#define STORAGE_CONTEXT_TAG "messaging-storage-context-tag"
static GQuark support_checked_quark;
static GQuark supported_quark;
static GQuark storage_context_quark;
/*****************************************************************************/
guint8
mm_iface_modem_messaging_get_local_multipart_reference (MMIfaceModemMessaging *self,
const gchar *number,
GError **error)
{
MMSmsList *list = NULL;
guint8 reference;
guint8 first;
/* Start by looking for a random number */
reference = g_random_int_range (1,255);
/* Then, look for the given reference in user-created messages */
g_object_get (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list,
NULL);
if (!list)
return reference;
first = reference;
do {
if (!mm_sms_list_has_local_multipart_reference (list, number, reference)) {
g_object_unref (list);
return reference;
}
if (reference == 255)
reference = 1;
else
reference++;
}
while (reference != first);
g_object_unref (list);
/* We were not able to find a new valid multipart reference :/
* return an error */
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_TOO_MANY,
"Cannot create multipart SMS: No valid multipart reference "
"available for destination number '%s'",
number);
return 0;
}
/*****************************************************************************/
void
mm_iface_modem_messaging_bind_simple_status (MMIfaceModemMessaging *self,
MMSimpleStatus *status)
{
}
/*****************************************************************************/
MMBaseSms *
mm_iface_modem_messaging_create_sms (MMIfaceModemMessaging *self)
{
g_assert (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->create_sms != NULL);
return MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->create_sms (self);
}
/*****************************************************************************/
typedef struct {
GArray *supported_mem1;
GArray *supported_mem2;
GArray *supported_mem3;
} StorageContext;
static void
storage_context_free (StorageContext *ctx)
{
if (ctx->supported_mem1)
g_array_unref (ctx->supported_mem1);
if (ctx->supported_mem2)
g_array_unref (ctx->supported_mem2);
if (ctx->supported_mem3)
g_array_unref (ctx->supported_mem3);
g_free (ctx);
}
static StorageContext *
get_storage_context (MMIfaceModemMessaging *self)
{
StorageContext *ctx;
if (G_UNLIKELY (!storage_context_quark))
storage_context_quark = (g_quark_from_static_string (
STORAGE_CONTEXT_TAG));
ctx = g_object_get_qdata (G_OBJECT (self), storage_context_quark);
if (!ctx) {
/* Create context and keep it as object data */
ctx = g_new0 (StorageContext, 1);
g_object_set_qdata_full (
G_OBJECT (self),
storage_context_quark,
ctx,
(GDestroyNotify)storage_context_free);
}
return ctx;
}
/*****************************************************************************/
typedef struct {
MmGdbusModemMessaging *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModemMessaging *self;
gchar *path;
} HandleDeleteContext;
static void
handle_delete_context_free (HandleDeleteContext *ctx)
{
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx->path);
g_free (ctx);
}
static void
handle_delete_ready (MMSmsList *list,
GAsyncResult *res,
HandleDeleteContext *ctx)
{
GError *error = NULL;
if (!mm_sms_list_delete_sms_finish (list, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_modem_messaging_complete_delete (ctx->skeleton, ctx->invocation);
handle_delete_context_free (ctx);
}
static void
handle_delete_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleDeleteContext *ctx)
{
MMModemState modem_state = MM_MODEM_STATE_UNKNOWN;
MMSmsList *list = NULL;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_delete_context_free (ctx);
return;
}
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state < MM_MODEM_STATE_ENABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot delete SMS: device not yet enabled");
handle_delete_context_free (ctx);
return;
}
g_object_get (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list,
NULL);
if (!list) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot delete SMS: missing SMS list");
handle_delete_context_free (ctx);
return;
}
mm_sms_list_delete_sms (list,
ctx->path,
(GAsyncReadyCallback)handle_delete_ready,
ctx);
g_object_unref (list);
}
static gboolean
handle_delete (MmGdbusModemMessaging *skeleton,
GDBusMethodInvocation *invocation,
const gchar *path,
MMIfaceModemMessaging *self)
{
HandleDeleteContext *ctx;
ctx = g_new (HandleDeleteContext, 1);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
ctx->path = g_strdup (path);
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_MESSAGING,
(GAsyncReadyCallback)handle_delete_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
typedef struct {
MmGdbusModemMessaging *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModemMessaging *self;
GVariant *dictionary;
} HandleCreateContext;
static void
handle_create_context_free (HandleCreateContext *ctx)
{
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_variant_unref (ctx->dictionary);
g_free (ctx);
}
static void
handle_create_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleCreateContext *ctx)
{
MMModemState modem_state = MM_MODEM_STATE_UNKNOWN;
MMSmsList *list = NULL;
GError *error = NULL;
MMSmsProperties *properties;
MMBaseSms *sms;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_create_context_free (ctx);
return;
}
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state < MM_MODEM_STATE_ENABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot create SMS: device not yet enabled");
handle_create_context_free (ctx);
return;
}
/* Parse input properties */
properties = mm_sms_properties_new_from_dictionary (ctx->dictionary, &error);
if (!properties) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_create_context_free (ctx);
return;
}
sms = mm_base_sms_new_from_properties (MM_BASE_MODEM (self),
properties,
&error);
if (!sms) {
g_object_unref (properties);
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_create_context_free (ctx);
return;
}
g_object_get (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list,
NULL);
if (!list) {
g_object_unref (properties);
g_object_unref (sms);
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot create SMS: missing SMS list");
handle_create_context_free (ctx);
return;
}
/* Add it to the list */
mm_sms_list_add_sms (list, sms);
/* Complete the DBus call */
mm_gdbus_modem_messaging_complete_create (ctx->skeleton,
ctx->invocation,
mm_base_sms_get_path (sms));
g_object_unref (sms);
g_object_unref (properties);
g_object_unref (list);
handle_create_context_free (ctx);
}
static gboolean
handle_create (MmGdbusModemMessaging *skeleton,
GDBusMethodInvocation *invocation,
GVariant *dictionary,
MMIfaceModemMessaging *self)
{
HandleCreateContext *ctx;
ctx = g_new (HandleCreateContext, 1);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
ctx->dictionary = g_variant_ref (dictionary);
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_MESSAGING,
(GAsyncReadyCallback)handle_create_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
static gboolean
handle_list (MmGdbusModemMessaging *skeleton,
GDBusMethodInvocation *invocation,
MMIfaceModemMessaging *self)
{
GStrv paths;
MMSmsList *list = NULL;
MMModemState modem_state;
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state < MM_MODEM_STATE_ENABLED) {
g_dbus_method_invocation_return_error (invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot list SMS messages: "
"device not yet enabled");
return TRUE;
}
g_object_get (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list,
NULL);
if (!list) {
g_dbus_method_invocation_return_error (invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot list SMS: missing SMS list");
return TRUE;
}
paths = mm_sms_list_get_paths (list);
mm_gdbus_modem_messaging_complete_list (skeleton,
invocation,
(const gchar *const *)paths);
g_strfreev (paths);
g_object_unref (list);
return TRUE;
}
/*****************************************************************************/
gboolean
mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self,
MMSmsPart *sms_part,
MMSmsState state,
MMSmsStorage storage)
{
MMSmsList *list = NULL;
GError *error = NULL;
gboolean added;
g_object_get (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list,
NULL);
if (!list)
return FALSE;
added = mm_sms_list_take_part (list, sms_part, state, storage, &error);
if (!added) {
mm_dbg ("Couldn't take part in SMS list: '%s'", error->message);
g_error_free (error);
/* If part wasn't taken, we need to free the part ourselves */
mm_sms_part_free (sms_part);
}
g_object_unref (list);
return added;
}
/*****************************************************************************/
static gboolean
is_storage_supported (GArray *supported,
MMSmsStorage preferred,
const gchar *action,
GError **error)
{
guint i;
if (supported) {
for (i = 0; i < supported->len; i++) {
if (preferred == g_array_index (supported, MMSmsStorage, i))
return TRUE;
}
}
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Storage '%s' is not supported for %s",
mm_sms_storage_get_string (preferred),
action);
return FALSE;
}
gboolean
mm_iface_modem_messaging_is_storage_supported_for_storing (MMIfaceModemMessaging *self,
MMSmsStorage storage,
GError **error)
{
/* mem2 is for storing */
return is_storage_supported ((get_storage_context (self))->supported_mem2,
storage,
"storing",
error);
}
gboolean
mm_iface_modem_messaging_is_storage_supported_for_receiving (MMIfaceModemMessaging *self,
MMSmsStorage storage,
GError **error)
{
/* mem3 is for receiving */
return is_storage_supported ((get_storage_context (self))->supported_mem3,
storage,
"receiving",
error);
}
/*****************************************************************************/
static void
update_message_list (MmGdbusModemMessaging *skeleton,
MMSmsList *list)
{
gchar **paths;
paths = mm_sms_list_get_paths (list);
mm_gdbus_modem_messaging_set_messages (skeleton, (const gchar *const *)paths);
g_strfreev (paths);
}
static void
sms_added (MMSmsList *list,
const gchar *sms_path,
gboolean received,
MmGdbusModemMessaging *skeleton)
{
mm_dbg ("Added %s SMS at '%s'",
received ? "received" : "local",
sms_path);
update_message_list (skeleton, list);
mm_gdbus_modem_messaging_emit_added (skeleton, sms_path, received);
}
static void
sms_deleted (MMSmsList *list,
const gchar *sms_path,
MmGdbusModemMessaging *skeleton)
{
mm_dbg ("Deleted SMS at '%s'", sms_path);
update_message_list (skeleton, list);
mm_gdbus_modem_messaging_emit_deleted (skeleton, sms_path);
}
/*****************************************************************************/
typedef struct _DisablingContext DisablingContext;
static void interface_disabling_step (GTask *task);
typedef enum {
DISABLING_STEP_FIRST,
DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS,
DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS,
DISABLING_STEP_LAST
} DisablingStep;
struct _DisablingContext {
DisablingStep step;
MmGdbusModemMessaging *skeleton;
};
static void
disabling_context_free (DisablingContext *ctx)
{
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
g_free (ctx);
}
gboolean
mm_iface_modem_messaging_disable_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
disable_unsolicited_events_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
DisablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_disabling_step (task);
}
static void
cleanup_unsolicited_events_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
DisablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_disabling_step (task);
}
static void
interface_disabling_step (GTask *task)
{
MMIfaceModemMessaging *self;
DisablingContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case DISABLING_STEP_FIRST:
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
/* Allow cleaning up unsolicited events */
if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events (
self,
(GAsyncReadyCallback)disable_unsolicited_events_ready,
task);
return;
}
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
/* Allow cleaning up unsolicited events */
if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)cleanup_unsolicited_events_ready,
task);
return;
}
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_LAST:
/* Clear SMS list */
g_object_set (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, NULL,
NULL);
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
g_assert_not_reached ();
}
void
mm_iface_modem_messaging_disable (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
DisablingContext *ctx;
GTask *task;
ctx = g_new0 (DisablingContext, 1);
ctx->step = DISABLING_STEP_FIRST;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
g_object_get (self,
MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &ctx->skeleton,
NULL);
if (!ctx->skeleton) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't get interface skeleton");
g_object_unref (task);
return;
}
interface_disabling_step (task);
}
/*****************************************************************************/
typedef struct _EnablingContext EnablingContext;
static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
ENABLING_STEP_SETUP_SMS_FORMAT,
ENABLING_STEP_STORAGE_DEFAULTS,
ENABLING_STEP_LOAD_INITIAL_SMS_PARTS,
ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
ENABLING_STEP_LAST
} EnablingStep;
struct _EnablingContext {
EnablingStep step;
MmGdbusModemMessaging *skeleton;
guint mem1_storage_index;
};
static void
enabling_context_free (EnablingContext *ctx)
{
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
g_free (ctx);
}
gboolean
mm_iface_modem_messaging_enable_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_sms_format_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format_finish (self, res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_enabling_step (task);
}
static void load_initial_sms_parts_from_storages (GTask *task);
static void
load_initial_sms_parts_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
EnablingContext *ctx;
GError *error = NULL;
ctx = g_task_get_task_data (task);
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts_finish (self, res, &error);
if (error) {
StorageContext *storage_ctx;
storage_ctx = get_storage_context (self);
mm_dbg ("Couldn't load SMS parts from storage '%s': '%s'",
mm_sms_storage_get_string (g_array_index (storage_ctx->supported_mem1,
MMSmsStorage,
ctx->mem1_storage_index)),
error->message);
g_error_free (error);
}
/* Go on with the storage iteration */
ctx->mem1_storage_index++;
load_initial_sms_parts_from_storages (task);
}
static void
set_default_storage_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
EnablingContext *ctx;
GError *error = NULL;
if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage_finish (self, res, &error)) {
mm_warn ("Could not set default storage: '%s'", error->message);
g_error_free (error);
}
/* Go on with next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_enabling_step (task);
}
static void
load_initial_sms_parts_from_storages (GTask *task)
{
MMIfaceModemMessaging *self;
EnablingContext *ctx;
gboolean all_loaded = FALSE;
StorageContext *storage_ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
storage_ctx = get_storage_context (self);
if (!storage_ctx->supported_mem1 || ctx->mem1_storage_index >= storage_ctx->supported_mem1->len)
all_loaded = TRUE;
/* We'll skip the 'MT' storage, as that is a combination of 'SM' and 'ME'; but only if
* this is not the only one in the list. */
else if ((g_array_index (storage_ctx->supported_mem1,
MMSmsStorage,
ctx->mem1_storage_index) == MM_SMS_STORAGE_MT) &&
(storage_ctx->supported_mem1->len > 1)) {
ctx->mem1_storage_index++;
if (ctx->mem1_storage_index >= storage_ctx->supported_mem1->len)
all_loaded = TRUE;
}
if (all_loaded) {
/* Go on with next step */
ctx->step++;
interface_enabling_step (task);
return;
}
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts (
self,
g_array_index (storage_ctx->supported_mem1,
MMSmsStorage,
ctx->mem1_storage_index),
(GAsyncReadyCallback)load_initial_sms_parts_ready,
task);
}
static void
setup_unsolicited_events_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_enabling_step (task);
}
static void
enable_unsolicited_events_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
EnablingContext *ctx;
GError *error = NULL;
/* Not critical! */
if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) {
mm_dbg ("Couldn't enable unsolicited events: '%s'", error->message);
g_error_free (error);
}
/* Go on with next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_enabling_step (task);
}
static MMSmsStorage
get_best_initial_default_sms_storage (MMIfaceModemMessaging *self)
{
StorageContext *storage_ctx;
guint i;
MMSmsStorage default_storages_preference[] = {
MM_SMS_STORAGE_MT, /* MT=ME+SM */
MM_SMS_STORAGE_ME,
MM_SMS_STORAGE_SM,
MM_SMS_STORAGE_UNKNOWN
};
storage_ctx = get_storage_context (self);
for (i = 0; default_storages_preference[i] != MM_SMS_STORAGE_UNKNOWN; i++) {
/* Check if the requested storage is really supported in both mem2 and mem3 */
if (is_storage_supported (storage_ctx->supported_mem2, default_storages_preference[i], "storing", NULL) &&
is_storage_supported (storage_ctx->supported_mem3, default_storages_preference[i], "receiving", NULL)) {
break;
}
}
return default_storages_preference[i];
}
static MMSmsStorage
get_single_default_sms_storage (MMIfaceModemMessaging *self)
{
StorageContext *storage_ctx;
storage_ctx = get_storage_context (self);
/* If there is one single storage supported for storing and receiving, just
* use that one. */
if (storage_ctx->supported_mem2 && storage_ctx->supported_mem2->len == 1 &&
storage_ctx->supported_mem3 && storage_ctx->supported_mem3->len == 1) {
MMSmsStorage storing_default;
storing_default = g_array_index (storage_ctx->supported_mem2, MMSmsStorage, 0);
if (storing_default == g_array_index (storage_ctx->supported_mem3, MMSmsStorage, 0))
return storing_default;
}
return MM_SMS_STORAGE_UNKNOWN;
}
static void
interface_enabling_step (GTask *task)
{
MMIfaceModemMessaging *self;
EnablingContext *ctx;
/* Don't run new steps if we're cancelled */
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST: {
MMSmsList *list;
list = mm_sms_list_new (MM_BASE_MODEM (self));
g_object_set (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, list,
NULL);
/* Connect to list's signals */
g_signal_connect (list,
MM_SMS_ADDED,
G_CALLBACK (sms_added),
ctx->skeleton);
g_signal_connect (list,
MM_SMS_DELETED,
G_CALLBACK (sms_deleted),
ctx->skeleton);
g_object_unref (list);
/* Fall down to next step */
ctx->step++;
}
case ENABLING_STEP_SETUP_SMS_FORMAT:
/* Allow setting SMS format to use */
if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format_finish) {
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format (
self,
(GAsyncReadyCallback)setup_sms_format_ready,
task);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_STORAGE_DEFAULTS: {
MMSmsStorage default_storage;
/* Is there only one single storage supported? if so, we don't care if
* setting default storage is implemented or not. */
default_storage = get_single_default_sms_storage (self);
if (default_storage == MM_SMS_STORAGE_UNKNOWN)
default_storage = get_best_initial_default_sms_storage (self);
/* Already bound to the 'default-storage' property in the skeleton */
g_object_set (self,
MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, default_storage,
NULL);
if (default_storage == MM_SMS_STORAGE_UNKNOWN)
mm_info ("Cannot set default storage, none of the suggested ones supported");
else if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage_finish) {
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage (
self,
default_storage,
(GAsyncReadyCallback)set_default_storage_ready,
task);
return;
}
/* Fall down to next step */
ctx->step++;
}
case ENABLING_STEP_LOAD_INITIAL_SMS_PARTS:
/* Allow loading the initial list of SMS parts */
if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts_finish) {
load_initial_sms_parts_from_storages (task);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
/* Allow setting up unsolicited events */
if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events (
self,
(GAsyncReadyCallback)setup_unsolicited_events_ready,
task);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
/* Allow setting up unsolicited events */
if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events (
self,
(GAsyncReadyCallback)enable_unsolicited_events_ready,
task);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_LAST:
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
g_assert_not_reached ();
}
void
mm_iface_modem_messaging_enable (MMIfaceModemMessaging *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
EnablingContext *ctx;
GTask *task;
ctx = g_new0 (EnablingContext, 1);
ctx->step = ENABLING_STEP_FIRST;
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
g_object_get (self,
MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &ctx->skeleton,
NULL);
if (!ctx->skeleton) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't get interface skeleton");
g_object_unref (task);
return;
}
interface_enabling_step (task);
}
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
INITIALIZATION_STEP_CHECK_SUPPORT,
INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
INITIALIZATION_STEP_LOAD_SUPPORTED_STORAGES,
INITIALIZATION_STEP_INIT_CURRENT_STORAGES,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitializationContext {
MmGdbusModemMessaging *skeleton;
InitializationStep step;
};
static void
initialization_context_free (InitializationContext *ctx)
{
g_object_unref (ctx->skeleton);
g_free (ctx);
}
static void
skip_unknown_storages (GArray *mem)
{
guint i = mem->len;
if (!mem)
return;
/* Remove UNKNOWN from the list of supported storages */
while (i-- > 0) {
if (g_array_index (mem, MMSmsStorage, i) == MM_SMS_STORAGE_UNKNOWN)
g_array_remove_index (mem, i);
}
}
static void
load_supported_storages_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
InitializationContext *ctx;
StorageContext *storage_ctx;
GError *error = NULL;
ctx = g_task_get_task_data (task);
storage_ctx = get_storage_context (self);
if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages_finish (
self,
res,
&storage_ctx->supported_mem1,
&storage_ctx->supported_mem2,
&storage_ctx->supported_mem3,
&error)) {
mm_dbg ("Couldn't load supported storages: '%s'", error->message);
g_error_free (error);
} else {
gchar *mem1;
gchar *mem2;
gchar *mem3;
GArray *supported_storages;
guint i;
/* Never add unknown storages */
skip_unknown_storages (storage_ctx->supported_mem1);
skip_unknown_storages (storage_ctx->supported_mem2);
skip_unknown_storages (storage_ctx->supported_mem3);
mem1 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem1->data,
storage_ctx->supported_mem1->len);
mem2 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem2->data,
storage_ctx->supported_mem2->len);
mem3 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem3->data,
storage_ctx->supported_mem3->len);
mm_dbg ("Supported storages loaded:");
mm_dbg (" mem1 (list/read/delete) storages: '%s'", mem1);
mm_dbg (" mem2 (write/send) storages: '%s'", mem2);
mm_dbg (" mem3 (reception) storages: '%s'", mem3);
g_free (mem1);
g_free (mem2);
g_free (mem3);
/* We set in the interface the list of storages which are allowed for
* both write/send and receive */
supported_storages = g_array_sized_new (FALSE, FALSE, sizeof (guint32), storage_ctx->supported_mem2->len);
for (i = 0; i < storage_ctx->supported_mem2->len; i++) {
gboolean found = FALSE;
guint j;
for (j = 0; j < storage_ctx->supported_mem3->len && !found; j++) {
if (g_array_index (storage_ctx->supported_mem3, MMSmsStorage, j) ==
g_array_index (storage_ctx->supported_mem2, MMSmsStorage, i))
found = TRUE;
}
if (found) {
guint32 val;
val = g_array_index (storage_ctx->supported_mem2, MMSmsStorage, i);
g_array_append_val (supported_storages, val);
}
}
mm_gdbus_modem_messaging_set_supported_storages (
ctx->skeleton,
mm_common_sms_storages_garray_to_variant (supported_storages));
g_array_unref (supported_storages);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (task);
}
static void
check_support_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
InitializationContext *ctx;
GError *error = NULL;
if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support_finish (self,
res,
&error)) {
if (error) {
/* This error shouldn't be treated as critical */
mm_dbg ("Messaging support check failed: '%s'", error->message);
g_error_free (error);
}
} else {
/* Messaging is supported! */
g_object_set_qdata (G_OBJECT (self),
supported_quark,
GUINT_TO_POINTER (TRUE));
}
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_initialization_step (task);
}
static void
init_current_storages_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
GTask *task)
{
InitializationContext *ctx;
GError *error = NULL;
if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages_finish (
self,
res,
&error)) {
mm_dbg ("Couldn't initialize current storages: '%s'", error->message);
g_error_free (error);
} else {
mm_dbg ("Current storages initialized");
}
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_initialization_step (task);
}
static void
interface_initialization_step (GTask *task)
{
MMIfaceModemMessaging *self;
InitializationContext *ctx;
/* Don't run new steps if we're cancelled */
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
/* Setup quarks if we didn't do it before */
if (G_UNLIKELY (!support_checked_quark))
support_checked_quark = (g_quark_from_static_string (
SUPPORT_CHECKED_TAG));
if (G_UNLIKELY (!supported_quark))
supported_quark = (g_quark_from_static_string (
SUPPORTED_TAG));
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_CHECK_SUPPORT:
if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
support_checked_quark))) {
/* Set the checked flag so that we don't run it again */
g_object_set_qdata (G_OBJECT (self),
support_checked_quark,
GUINT_TO_POINTER (TRUE));
/* Initially, assume we don't support it */
g_object_set_qdata (G_OBJECT (self),
supported_quark,
GUINT_TO_POINTER (FALSE));
if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support_finish) {
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support (
self,
(GAsyncReadyCallback)check_support_ready,
task);
return;
}
/* If there is no implementation to check support, assume we DON'T
* support it. */
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
supported_quark))) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Messaging not supported");
g_object_unref (task);
return;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_LOAD_SUPPORTED_STORAGES:
if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages_finish) {
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages (
self,
(GAsyncReadyCallback)load_supported_storages_ready,
task);
return;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_INIT_CURRENT_STORAGES:
if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages &&
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages_finish) {
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages (
self,
(GAsyncReadyCallback)init_current_storages_ready,
task);
return;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
/* Handle method invocations */
g_signal_connect (ctx->skeleton,
"handle-create",
G_CALLBACK (handle_create),
self);
g_signal_connect (ctx->skeleton,
"handle-delete",
G_CALLBACK (handle_delete),
self);
g_signal_connect (ctx->skeleton,
"handle-list",
G_CALLBACK (handle_list),
self);
/* Finally, export the new interface */
mm_gdbus_object_skeleton_set_modem_messaging (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM_MESSAGING (ctx->skeleton));
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
g_assert_not_reached ();
}
gboolean
mm_iface_modem_messaging_initialize_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
void
mm_iface_modem_messaging_initialize (MMIfaceModemMessaging *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
InitializationContext *ctx;
MmGdbusModemMessaging *skeleton = NULL;
GTask *task;
/* Did we already create it? */
g_object_get (self,
MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &skeleton,
NULL);
if (!skeleton) {
skeleton = mm_gdbus_modem_messaging_skeleton_new ();
mm_gdbus_modem_messaging_set_supported_storages (skeleton, NULL);
/* Bind our Default messaging property */
g_object_bind_property (self, MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE,
skeleton, "default-storage",
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_set (self,
MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, skeleton,
NULL);
}
/* Perform async initialization here */
ctx = g_new0 (InitializationContext, 1);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
interface_initialization_step (task);
}
void
mm_iface_modem_messaging_shutdown (MMIfaceModemMessaging *self)
{
/* Unexport DBus interface and remove the skeleton */
mm_gdbus_object_skeleton_set_modem_messaging (MM_GDBUS_OBJECT_SKELETON (self), NULL);
g_object_set (self,
MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, NULL,
NULL);
}
/*****************************************************************************/
static void
iface_modem_messaging_init (gpointer g_iface)
{
static gboolean initialized = FALSE;
if (initialized)
return;
/* Properties */
g_object_interface_install_property
(g_iface,
g_param_spec_object (MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON,
"Messaging DBus skeleton",
"DBus skeleton for the Messaging interface",
MM_GDBUS_TYPE_MODEM_MESSAGING_SKELETON,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_object (MM_IFACE_MODEM_MESSAGING_SMS_LIST,
"SMS list",
"List of SMS objects managed in the interface",
MM_TYPE_SMS_LIST,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_boolean (MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE,
"PDU mode",
"Whether PDU mode should be used",
FALSE,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_enum (MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE,
"SMS default storage",
"Default storage to be used when storing/receiving SMS messages",
MM_TYPE_SMS_STORAGE,
MM_SMS_STORAGE_ME,
G_PARAM_READWRITE));
initialized = TRUE;
}
GType
mm_iface_modem_messaging_get_type (void)
{
static GType iface_modem_messaging_type = 0;
if (!G_UNLIKELY (iface_modem_messaging_type)) {
static const GTypeInfo info = {
sizeof (MMIfaceModemMessaging), /* class_size */
iface_modem_messaging_init, /* base_init */
NULL, /* base_finalize */
};
iface_modem_messaging_type = g_type_register_static (G_TYPE_INTERFACE,
"MMIfaceModemMessaging",
&info,
0);
g_type_interface_add_prerequisite (iface_modem_messaging_type, MM_TYPE_IFACE_MODEM);
}
return iface_modem_messaging_type;
}