Commit 85d3c47d authored by Slava Monich's avatar Slava Monich

[mms-engine] Proper support for multiple SIMs. Fixes JB#33702

We need to use org.nemomobile.ofono.ModemManager API to notify ofono
that we need to download MMS so that it enables mobile data for the
appropriate SIM. If it's different from the currently selected data SIM,
then connman will be prevented from using mobile data while MMS download
is taking place.
parent 9ed94503
# -*- Mode: makefile-gmake -*-
.PHONY: clean all debug release
# Required packages
PKGS = libgofono libgofonoext libglibutil glib-2.0 gio-2.0 gio-unix-2.0
#
# Default target
#
all: debug release
#
# Sources
#
SRC = \
mms_connection_nemo.c \
mms_connman_nemo.c
#
# Directories
#
SRC_DIR = src
INCLUDE_DIR = include
BUILD_DIR = build
SPEC_DIR = spec
MMS_LIB_INCLUDE = ../mms-lib/include
DEBUG_BUILD_DIR = $(BUILD_DIR)/debug
RELEASE_BUILD_DIR = $(BUILD_DIR)/release
#
# Tools and flags
#
CC = $(CROSS_COMPILE)gcc
LD = $(CC)
ARFLAGS = rc
DEBUG_FLAGS = -g
RELEASE_FLAGS = -O2
DEBUG_DEFS = -DDEBUG
RELEASE_DEFS =
WARNINGS = -Wall
INCLUDES = -I$(SRC_DIR) -I$(INCLUDE_DIR) -I$(MMS_LIB_INCLUDE) -I.
CFLAGS += $(WARNINGS) $(INCLUDES) $(shell pkg-config --cflags $(PKGS)) \
-fPIC -MMD
ifndef KEEP_SYMBOLS
KEEP_SYMBOLS = 0
endif
ifneq ($(KEEP_SYMBOLS),0)
RELEASE_FLAGS += -g
endif
DEBUG_CFLAGS = $(DEBUG_FLAGS) $(DEBUG_DEFS) $(CFLAGS)
RELEASE_CFLAGS = $(RELEASE_FLAGS) $(RELEASE_DEFS) $(CFLAGS)
#
# Files
#
DEBUG_OBJS = $(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o)
RELEASE_OBJS = $(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o)
#
# Dependencies
#
DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d)
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(strip $(DEPS)),)
-include $(DEPS)
endif
endif
$(DEBUG_OBJS): | $(DEBUG_BUILD_DIR)
$(RELEASE_OBJS): | $(RELEASE_BUILD_DIR)
#
# Rules
#
LIB = libmms-connman-nemo.a
DEBUG_LIB = $(DEBUG_BUILD_DIR)/$(LIB)
RELEASE_LIB = $(RELEASE_BUILD_DIR)/$(LIB)
debug: $(DEBUG_LIB)
release: $(RELEASE_LIB)
clean:
rm -fr $(BUILD_DIR) *~ $(SRC_DIR)/*~ $(INCLUDE_DIR)/*~
$(DEBUG_BUILD_DIR):
mkdir -p $@
$(RELEASE_BUILD_DIR):
mkdir -p $@
$(DEBUG_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
$(CC) -c $(WARN) $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(RELEASE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
$(CC) -c $(WARN) $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(DEBUG_LIB): $(DEBUG_OBJS)
$(AR) $(ARFLAGS) $@ $^
$(RELEASE_LIB): $(RELEASE_OBJS)
$(AR) $(ARFLAGS) $@ $^
/*
* Copyright (C) 2013-2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* 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.
*
*/
#ifndef JOLLA_MMS_CONNMAN_NEMO_H
#define JOLLA_MMS_CONNMAN_NEMO_H
#include "mms_connman.h"
MMSConnMan*
mms_connman_nemo_new(void);
#endif /* JOLLA_MMS_CONNMAN_NEMO_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/
/*
* Copyright (C) 2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* 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.
*
*/
#ifndef JOLLA_MMS_CONNMAN_NEMO_LOG_H
#define JOLLA_MMS_CONNMAN_NEMO_LOG_H
#include "mms_log.h"
#define MMS_CONNMAN_LOG_MODULES(log) \
log(gofonoext_log) \
log(gofono_log)
MMS_CONNMAN_LOG_MODULES(GLOG_MODULE_DECL)
#endif /* JOLLA_MMS_CONNMAN_NEMO_LOG_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/
TEMPLATE = lib
CONFIG += staticlib
CONFIG -= qt
CONFIG += link_pkgconfig
PKGCONFIG += libgofono libgofonoext libglibutil glib-2.0 gio-2.0 gio-unix-2.0
DBUS_SPEC_DIR = $$_PRO_FILE_PWD_/spec
INCLUDEPATH += include
INCLUDEPATH += ../mms-lib/include
QMAKE_CFLAGS += -Wno-unused-parameter
CONFIG(debug, debug|release) {
DEFINES += DEBUG
DESTDIR = $$_PRO_FILE_PWD_/build/debug
} else {
DESTDIR = $$_PRO_FILE_PWD_/build/release
}
SOURCES += \
src/mms_connection_nemo.c \
src/mms_connman_nemo.c
HEADERS += \
src/mms_connection_nemo.h
HEADERS += \
include/mms_connman_nemo.h \
include/mms_connman_nemo_log.h
/*
* Copyright (C) 2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* 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.
*
*/
#include "mms_connection_nemo.h"
#include "mms_connman.h"
#include <gofono_connmgr.h>
#include <gofono_connctx.h>
/* Logging */
#define MMS_LOG_MODULE_NAME mms_connection_log
#include "mms_connman_nemo_log.h"
MMS_LOG_MODULE_DEFINE("mms-connection-nemo");
enum mm_handler_id {
MM_HANDLER_VALID,
MM_HANDLER_MMS_IMSI,
MM_HANDLER_COUNT
};
enum connmgr_handler_id {
CONNMGR_HANDLER_VALID,
CONNMGR_HANDLER_ATTACHED,
CONNMGR_HANDLER_COUNT
};
enum context_handler_id {
CONTEXT_HANDLER_VALID,
CONTEXT_HANDLER_ACTIVE,
CONTEXT_HANDLER_INTERFACE,
CONTEXT_HANDLER_MMS_PROXY,
CONTEXT_HANDLER_MMS_CENTER,
CONTEXT_HANDLER_ACTIVATE_FAILED,
CONTEXT_HANDLER_COUNT
};
typedef struct mms_connection_nemo {
MMSConnection connection;
MMSConnMan* cm;
OfonoExtModemManager* mm;
OfonoConnCtx* context;
OfonoConnMgr* connmgr;
gulong mm_handler_id[MM_HANDLER_COUNT];
gulong context_handler_id[CONTEXT_HANDLER_COUNT];
gulong connmgr_handler_id[CONNMGR_HANDLER_COUNT];
char* imsi;
char* path;
} MMSConnectionNemo;
typedef MMSConnectionClass MMSConnectionNemoClass;
G_DEFINE_TYPE(MMSConnectionNemo, mms_connection_nemo, MMS_TYPE_CONNECTION)
#define MMS_TYPE_CONNECTION_NEMO (mms_connection_nemo_get_type())
#define MMS_CONNECTION_NEMO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
MMS_TYPE_CONNECTION_NEMO, MMSConnectionNemo))
static
void
mms_connection_nemo_disconnect(
MMSConnectionNemo* self)
{
ofonoext_mm_remove_handlers(self->mm, self->mm_handler_id,
G_N_ELEMENTS(self->mm_handler_id));
ofono_connmgr_remove_handlers(self->connmgr, self->connmgr_handler_id,
G_N_ELEMENTS(self->connmgr_handler_id));
ofono_connctx_remove_handlers(self->context, self->context_handler_id,
G_N_ELEMENTS(self->context_handler_id));
}
static
void
mms_connection_nemo_disconnect_connmgr(
MMSConnectionNemo* self,
enum connmgr_handler_id id)
{
if (self->connmgr_handler_id[id]) {
ofono_connmgr_remove_handler(self->connmgr,
self->connmgr_handler_id[id]);
self->connmgr_handler_id[id] = 0;
}
}
static
void
mms_connection_nemo_disconnect_context(
MMSConnectionNemo* self,
enum context_handler_id id)
{
if (self->context_handler_id[id]) {
ofono_connctx_remove_handler(self->context,
self->context_handler_id[id]);
self->context_handler_id[id] = 0;
}
}
static
void
mms_connection_nemo_reset_mms_imsi_done(
OfonoExtModemManager* mm,
const char* path,
const GError* error,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
MMS_DEBUG("Released %s", self->path);
mms_connman_busy_dec(self->cm);
mms_connection_unref(&self->connection);
}
static
gboolean
mms_connection_nemo_set_state(
MMSConnectionNemo* self,
MMS_CONNECTION_STATE state)
{
if (self->connection.state != state) {
if (self->connection.state == MMS_CONNECTION_STATE_FAILED ||
self->connection.state == MMS_CONNECTION_STATE_CLOSED) {
/* These are terminal states, can't change those */
return FALSE;
} else if (self->connection.state > state) {
/* Can't move back to a previous state */
return FALSE;
}
if (state == MMS_CONNECTION_STATE_FAILED ||
state == MMS_CONNECTION_STATE_CLOSED) {
if (self->path) {
/* Tell ofono that this SIM is no longer reserved for MMS.
* Bump the reference while this D-Bus call is pending */
mms_connection_ref(&self->connection);
mms_connman_busy_inc(self->cm);
ofonoext_mm_set_mms_imsi_full(self->mm, "",
mms_connection_nemo_reset_mms_imsi_done, self);
}
/* Stop listening for property changes */
mms_connection_nemo_disconnect(self);
}
self->connection.state = state;
mms_connection_signal_state_change(&self->connection);
}
return TRUE;
}
static
void
mms_connection_nemo_cancel(
MMSConnectionNemo* self)
{
if ((self->connection.state <= MMS_CONNECTION_STATE_OPENING &&
mms_connection_nemo_set_state(self, MMS_CONNECTION_STATE_FAILED)) ||
mms_connection_nemo_set_state(self, MMS_CONNECTION_STATE_CLOSED)) {
MMS_DEBUG("Cancelled %s", self->connection.imsi);
}
}
static
void
mms_connection_nemo_cancel_if_invalid(
OfonoObject* obj,
void* arg)
{
if (!obj->valid) {
MMS_VERBOSE_("oops!");
mms_connection_nemo_cancel(MMS_CONNECTION_NEMO(arg));
}
}
static
void
mms_connection_nemo_active_changed(
OfonoConnCtx* context,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
MMS_ASSERT(self->context == context);
if (context->active) {
MMS_DEBUG("Connection %s opened", self->connection.imsi);
mms_connection_nemo_set_state(self, MMS_CONNECTION_STATE_OPEN);
} else {
mms_connection_nemo_disconnect_context(self, CONTEXT_HANDLER_ACTIVE);
mms_connection_nemo_set_state(self, MMS_CONNECTION_STATE_CLOSED);
}
}
static
void
mms_connection_nemo_interface_changed(
OfonoConnCtx* context,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
MMS_ASSERT(self->context == context);
if (context->ifname) {
self->connection.netif = context->ifname;
MMS_DEBUG("Interface: %s", self->connection.netif);
} else {
self->connection.netif = NULL;
}
}
static
void
mms_connection_nemo_mms_proxy_changed(
OfonoConnCtx* context,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
MMS_ASSERT(self->context == context);
if (context->mms_proxy) {
self->connection.mmsproxy = context->mms_proxy;
MMS_DEBUG("MessageProxy: %s", self->connection.mmsproxy);
} else {
self->connection.mmsproxy = NULL;
}
}
static
void
mms_connection_nemo_mms_center_changed(
OfonoConnCtx* context,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
MMS_ASSERT(self->context == context);
if (context->mms_center) {
self->connection.mmsc = context->mms_center;
MMS_DEBUG("MessageCenter: %s", self->connection.mmsc);
} else {
self->connection.mmsc = NULL;
}
}
static
void
mms_connection_nemo_activate_failed(
OfonoConnCtx* context,
const GError* error,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
MMS_ASSERT(self->context == context);
mms_connection_nemo_cancel(self);
}
static
void
mms_connection_nemo_check_context(
MMSConnectionNemo* self)
{
if (self->connection.state == MMS_CONNECTION_STATE_OPENING) {
OfonoConnCtx* context = self->context;
if (ofono_connctx_valid(context) && context->active) {
/* Already connected */
mms_connection_nemo_set_state(self, MMS_CONNECTION_STATE_OPEN);
} else {
OfonoConnMgr* connmgr = self->connmgr;
if (ofono_connmgr_valid(connmgr) && connmgr->attached) {
self->context_handler_id[CONTEXT_HANDLER_ACTIVATE_FAILED] =
ofono_connctx_add_activate_failed_handler(context,
mms_connection_nemo_activate_failed, self);
MMS_DEBUG("Activate %s", ofono_connctx_path(context));
ofono_connctx_activate(context);
}
}
}
}
static
void
mms_connection_nemo_connmgr_attached_changed(
OfonoConnMgr* connmgr,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
MMS_DEBUG("Data registration %s for %s", connmgr->attached ? "on" : "off",
ofono_connmgr_path(connmgr));
mms_connection_nemo_check_context(self);
}
static
void
mms_connection_nemo_setup_context(
MMSConnectionNemo* self)
{
MMSConnection* conn = &self->connection;
OfonoConnCtx* context = self->context;
/* From this point on, cancel the connection if OfonoConnMgr becomes
* invalid (which would probably mean SIM removal or ofono crash) */
mms_connection_nemo_disconnect_context(self, CONTEXT_HANDLER_VALID);
self->context_handler_id[CONTEXT_HANDLER_VALID] =
ofono_object_add_valid_changed_handler(
ofono_connctx_object(self->context),
mms_connection_nemo_cancel_if_invalid, self);
/* Capture the current context state */
if (context->mms_proxy && context->mms_proxy[0]) {
conn->mmsproxy = context->mms_proxy;
MMS_DEBUG("MessageProxy: %s", conn->mmsproxy);
}
if (context->mms_center && context->mms_center[0]) {
conn->mmsc = context->mms_center;
MMS_DEBUG("MessageCenter: %s", conn->mmsc);
}
if (context->ifname && context->ifname[0]) {
conn->netif = context->ifname;
MMS_DEBUG("Interface: %s", conn->netif);
}
/* Track context property changes */
self->context_handler_id[CONTEXT_HANDLER_ACTIVE] =
ofono_connctx_add_active_changed_handler(context,
mms_connection_nemo_active_changed, self);
self->context_handler_id[CONTEXT_HANDLER_INTERFACE] =
ofono_connctx_add_interface_changed_handler(context,
mms_connection_nemo_interface_changed, self);
self->context_handler_id[CONTEXT_HANDLER_MMS_PROXY] =
ofono_connctx_add_mms_proxy_changed_handler(context,
mms_connection_nemo_mms_proxy_changed, self);
self->context_handler_id[CONTEXT_HANDLER_MMS_CENTER] =
ofono_connctx_add_mms_center_changed_handler(context,
mms_connection_nemo_mms_center_changed, self);
/* And start tracking the data registration state */
self->connmgr_handler_id[CONNMGR_HANDLER_ATTACHED] =
ofono_connmgr_add_attached_changed_handler(self->connmgr,
mms_connection_nemo_connmgr_attached_changed, self);
/* Check if we can actually connect */
mms_connection_nemo_check_context(self);
}
static
void
mms_connection_nemo_context_valid_changed(
OfonoConnCtx* context,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
GASSERT(self->connection.state == MMS_CONNECTION_STATE_OPENING);
if (ofono_connctx_valid(context)) {
mms_connection_nemo_setup_context(self);
}
}
static
void
mms_connection_nemo_init_context(
MMSConnectionNemo* self)
{
MMS_ASSERT(ofono_connmgr_valid(self->connmgr));
MMS_ASSERT(!self->context);
/* From this point on, cancel the connection if OfonoConnMgr becomes
* invalid (which would probably mean SIM removal or ofono crash) */
mms_connection_nemo_disconnect_connmgr(self, CONNMGR_HANDLER_VALID);
self->connmgr_handler_id[CONNMGR_HANDLER_VALID] =
ofono_object_add_valid_changed_handler(
ofono_connmgr_object(self->connmgr),
mms_connection_nemo_cancel_if_invalid, self);
/* ofono_connctx_ref has no problem with a NULL pointer */
self->context = ofono_connctx_ref(ofono_connmgr_get_context_for_type(
self->connmgr, OFONO_CONNCTX_TYPE_MMS));
if (self->context) {
MMS_DEBUG("MMS context %s", ofono_connctx_path(self->context));
if (ofono_connctx_valid(self->context)) {
mms_connection_nemo_setup_context(self);
} else {
self->context_handler_id[CONTEXT_HANDLER_VALID] =
ofono_connctx_add_valid_changed_handler(self->context,
mms_connection_nemo_context_valid_changed, self);
}
} else {
MMS_WARN("No MMS context");
mms_connection_nemo_cancel(self);
}
}
static
void
mms_connection_nemo_connmgr_valid_changed(
OfonoConnMgr* connmgr,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
GASSERT(self->connection.state == MMS_CONNECTION_STATE_OPENING);
if (ofono_connmgr_valid(connmgr)) {
mms_connection_nemo_init_context(self);
}
}
static
void
mms_connection_nemo_mms_imsi_changed(
OfonoExtModemManager* mm,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
if (mm->valid && g_strcmp0(mm->mms_imsi, self->connection.imsi)) {
MMS_VERBOSE_("%s", mm->mms_imsi);
mms_connection_nemo_cancel(self);
}
}
static
void
mms_connection_nemo_set_mms_imsi_done(
OfonoExtModemManager* mm,
const char* path,
const GError* error,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
if (error) {
mms_connection_nemo_cancel(self);
} else {
MMS_DEBUG("MMS modem path %s", path);
MMS_ASSERT(!self->path);
MMS_ASSERT(!self->connmgr);
self->path = g_strdup(path);
if (self->connection.state == MMS_CONNECTION_STATE_OPENING) {
self->connmgr = ofono_connmgr_new(path);
/* Cancel connection if MMS SIM changes */
self->mm_handler_id[MM_HANDLER_MMS_IMSI] =
ofonoext_mm_add_mms_imsi_changed_handler(mm,
mms_connection_nemo_mms_imsi_changed, self);
if (ofono_connmgr_valid(self->connmgr)) {
mms_connection_nemo_init_context(self);
} else {
/* Wait for OfonoConnMgr to become valid */
self->connmgr_handler_id[CONNMGR_HANDLER_VALID] =
ofono_connmgr_add_valid_changed_handler(self->connmgr,
mms_connection_nemo_connmgr_valid_changed, self);
}
} else {
/* Connection has been cancelled, release the slot */
MMS_DEBUG("Canceled, releasing %s", path);
mms_connection_ref(&self->connection);
mms_connman_busy_inc(self->cm);
ofonoext_mm_set_mms_imsi_full(self->mm, "",
mms_connection_nemo_reset_mms_imsi_done, self);
}
}
/* Release the reference */
mms_connman_busy_dec(self->cm);
mms_connection_unref(&self->connection);
}
static
void
mms_connection_nemo_cancel_if_mm_invalid(
OfonoExtModemManager* mm,
void* arg)
{
if (!mm->valid) {
MMS_VERBOSE_("oops!");
mms_connection_nemo_cancel(MMS_CONNECTION_NEMO(arg));
}
}
static
void
mms_connection_nemo_request_sim(
MMSConnectionNemo* self)
{
/* Cancel the connection if OfonoExtModemManager becomes invalid */
GASSERT(self->mm->valid);
ofonoext_mm_remove_handler(self->mm,
self->mm_handler_id[MM_HANDLER_VALID]);
self->mm_handler_id[MM_HANDLER_VALID] =
ofonoext_mm_add_valid_changed_handler(self->mm,
mms_connection_nemo_cancel_if_mm_invalid, self);
/* Bump the reference while this D-Bus call is pending */
mms_connection_ref(&self->connection);
mms_connman_busy_inc(self->cm);
ofonoext_mm_set_mms_imsi_full(self->mm, self->imsi,
mms_connection_nemo_set_mms_imsi_done, self);
}
static
void
mms_connection_nemo_mm_valid_changed(
OfonoExtModemManager* mm,
void* arg)
{
MMSConnectionNemo* self = MMS_CONNECTION_NEMO(arg);
MMS_VERBOSE_("%p %d", self, mm->valid);
if (mm->valid) {
mms_connection_nemo_request_sim(arg);
}
}
MMSConnection*
mms_connection_nemo_new(
MMSConnMan* cm,
const char* imsi,
gboolean user_request)
{
MMSConnectionNemo* self = g_object_new(MMS_TYPE_CONNECTION_NEMO, NULL);