/* -*- 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) 2009 Novell, Inc. * Copyright (C) 2009 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google, Inc. */ #include #define _LIBMM_INSIDE_MM #include #include "mm-bearer-list.h" #include "mm-sim.h" #include "mm-error-helpers.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem-simple.h" #include "mm-log.h" /*****************************************************************************/ /* Register in either a CDMA or a 3GPP network (or both) */ typedef struct { GSimpleAsyncResult *result; MMIfaceModemSimple *self; gchar *operator_id; guint remaining_tries_cdma; guint remaining_tries_3gpp; guint max_try_time; } RegisterInNetworkContext; static void register_in_network_context_complete_and_free (RegisterInNetworkContext *ctx) { g_simple_async_result_complete_in_idle (ctx->result); g_free (ctx->operator_id); g_object_unref (ctx->result); g_object_unref (ctx->self); g_free (ctx); } static gboolean register_in_3gpp_or_cdma_network_finish (MMIfaceModemSimple *self, GAsyncResult *res, GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } static void check_next_registration (RegisterInNetworkContext *ctx); static void register_in_cdma_network_ready (MMIfaceModemCdma *self, GAsyncResult *res, RegisterInNetworkContext *ctx) { ctx->remaining_tries_cdma--; if (!mm_iface_modem_cdma_register_in_network_finish ( MM_IFACE_MODEM_CDMA (self), res, NULL)) { /* Retry check */ check_next_registration (ctx); return; } /* Registered we are! */ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); register_in_network_context_complete_and_free (ctx); } static void register_in_3gpp_network_ready (MMIfaceModem3gpp *self, GAsyncResult *res, RegisterInNetworkContext *ctx) { ctx->remaining_tries_3gpp--; if (!mm_iface_modem_3gpp_register_in_network_finish ( MM_IFACE_MODEM_3GPP (self), res, NULL)) { /* Retry check */ check_next_registration (ctx); return; } /* Registered we are! */ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); register_in_network_context_complete_and_free (ctx); } static void check_next_registration (RegisterInNetworkContext *ctx) { if (ctx->remaining_tries_cdma > ctx->remaining_tries_3gpp && ctx->remaining_tries_cdma > 0) { mm_iface_modem_cdma_register_in_network ( MM_IFACE_MODEM_CDMA (ctx->self), ctx->max_try_time, (GAsyncReadyCallback)register_in_cdma_network_ready, ctx); return; } if (ctx->remaining_tries_3gpp > 0) { mm_iface_modem_3gpp_register_in_network ( MM_IFACE_MODEM_3GPP (ctx->self), ctx->operator_id, ctx->max_try_time, (GAsyncReadyCallback)register_in_3gpp_network_ready, ctx); return; } /* No more tries of anything */ g_simple_async_result_take_error ( ctx->result, mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT)); register_in_network_context_complete_and_free (ctx); } static void register_in_3gpp_or_cdma_network (MMIfaceModemSimple *self, const gchar *operator_id, GAsyncReadyCallback callback, gpointer user_data) { RegisterInNetworkContext *ctx; ctx = g_new0 (RegisterInNetworkContext, 1); ctx->self = g_object_ref (self); ctx->operator_id = g_strdup (operator_id); ctx->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, register_in_3gpp_or_cdma_network); /* 3GPP-only modems... */ if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (ctx->self))) { ctx->max_try_time = 60; ctx->remaining_tries_cdma = 0; ctx->remaining_tries_3gpp = 1; } /* CDMA-only modems... */ else if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (ctx->self))) { ctx->max_try_time = 60; ctx->remaining_tries_cdma = 1; ctx->remaining_tries_3gpp = 0; } /* Mixed 3GPP+CDMA modems... */ else { ctx->max_try_time = 10; ctx->remaining_tries_cdma = 6; ctx->remaining_tries_3gpp = 6; } check_next_registration (ctx); } /*****************************************************************************/ typedef enum { CONNECTION_STEP_FIRST, CONNECTION_STEP_UNLOCK_CHECK, CONNECTION_STEP_WAIT_FOR_INITIALIZED, CONNECTION_STEP_ENABLE, CONNECTION_STEP_WAIT_FOR_ENABLED, CONNECTION_STEP_REGISTER, CONNECTION_STEP_BEARER, CONNECTION_STEP_CONNECT, CONNECTION_STEP_LAST } ConnectionStep; typedef struct { MmGdbusModemSimple *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemSimple *self; ConnectionStep step; /* Expected input properties */ GVariant *dictionary; MMSimpleConnectProperties *properties; /* Results to set */ MMBearer *bearer; } ConnectionContext; static void connection_context_free (ConnectionContext *ctx) { g_variant_unref (ctx->dictionary); if (ctx->properties) g_object_unref (ctx->properties); if (ctx->bearer) g_object_unref (ctx->bearer); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx); } static void connection_step (ConnectionContext *ctx); static void connect_bearer_ready (MMBearer *bearer, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; if (!mm_bearer_connect_finish (bearer, res, &error)) { mm_dbg ("Couldn't connect bearer: '%s'", error->message); g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* Bearer connected.... all done!!!!! */ ctx->step++; connection_step (ctx); } static void create_bearer_ready (MMIfaceModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; /* ownership for the caller */ ctx->bearer = mm_iface_modem_create_bearer_finish (self, res, &error); if (!ctx->bearer) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* Bearer available! */ ctx->step++; connection_step (ctx); } static void register_in_3gpp_or_cdma_network_ready (MMIfaceModemSimple *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; if (!register_in_3gpp_or_cdma_network_finish (self, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* Registered now! */ ctx->step++; connection_step (ctx); } static void wait_for_enabled_ready (MMIfaceModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; MMModemState state; state = mm_iface_modem_wait_for_final_state_finish (self, res, &error); if (error) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } if (state < MM_MODEM_STATE_ENABLED) { g_dbus_method_invocation_return_error ( ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Couldn't wait for 'enabled': new state is '%s'", mm_modem_state_get_string (state)); connection_context_free (ctx); return; } /* Enabled now, cool. */ ctx->step++; connection_step (ctx); } static void enable_ready (MMBaseModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; if (!mm_base_modem_enable_finish (MM_BASE_MODEM (self), res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* Enabling done!, keep on!!! */ ctx->step++; connection_step (ctx); } static void wait_for_initialized_ready (MMIfaceModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; mm_iface_modem_wait_for_final_state_finish (self, res, &error); if (error) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* Initialized now, cool. */ ctx->step++; connection_step (ctx); } static void send_pin_ready (MMSim *sim, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; if (!mm_sim_send_pin_finish (sim, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* Sent pin, cool. */ ctx->step++; connection_step (ctx); } static void update_lock_info_ready (MMIfaceModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; MMModemLock lock; MMSim *sim; lock = mm_iface_modem_update_lock_info_finish (self, res, &error); if (error) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* If we are already unlocked, go on to next step. Note that we do also * allow SIM-PIN2, as we don't need to unlock that in order to get * connected. */ if (lock == MM_MODEM_LOCK_NONE || lock == MM_MODEM_LOCK_SIM_PIN2) { ctx->step++; connection_step (ctx); return; } /* During simple connect we are only allowed to use SIM PIN */ if (lock != MM_MODEM_LOCK_SIM_PIN || !mm_simple_connect_properties_get_pin (ctx->properties)) { g_dbus_method_invocation_return_error ( ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED, "Modem is locked with '%s' code; cannot unlock it", mm_modem_lock_get_string (lock)); connection_context_free (ctx); return; } /* Try to unlock the modem providing the PIN */ sim = NULL; g_object_get (ctx->self, MM_IFACE_MODEM_SIM, &sim, NULL); if (!sim) { g_dbus_method_invocation_return_error ( ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot unlock modem, couldn't get access to the SIM"); connection_context_free (ctx); return; } mm_sim_send_pin (sim, mm_simple_connect_properties_get_pin (ctx->properties), (GAsyncReadyCallback)send_pin_ready, ctx); g_object_unref (sim); } typedef struct { MMBearer *found; } BearerListFindContext; static void bearer_list_find_disconnected (MMBearer *bearer, BearerListFindContext *ctx) { /* If already marked one to remove, do nothing */ if (ctx->found) return; if (mm_bearer_get_status (bearer) == MM_BEARER_STATUS_DISCONNECTED) ctx->found = g_object_ref (bearer); } static void connection_step (ConnectionContext *ctx) { switch (ctx->step) { case CONNECTION_STEP_FIRST: /* Fall down to next step */ ctx->step++; case CONNECTION_STEP_UNLOCK_CHECK: mm_info ("Simple connect state (%d/%d): Unlock check", ctx->step, CONNECTION_STEP_LAST); mm_iface_modem_update_lock_info (MM_IFACE_MODEM (ctx->self), MM_MODEM_LOCK_UNKNOWN, /* ask */ (GAsyncReadyCallback)update_lock_info_ready, ctx); return; case CONNECTION_STEP_WAIT_FOR_INITIALIZED: mm_info ("Simple connect state (%d/%d): Wait to get fully initialized", ctx->step, CONNECTION_STEP_LAST); mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_DISABLED, /* disabled == initialized */ (GAsyncReadyCallback)wait_for_initialized_ready, ctx); return; case CONNECTION_STEP_ENABLE: mm_info ("Simple connect state (%d/%d): Enable", ctx->step, CONNECTION_STEP_LAST); mm_base_modem_enable (MM_BASE_MODEM (ctx->self), (GAsyncReadyCallback)enable_ready, ctx); return; case CONNECTION_STEP_WAIT_FOR_ENABLED: mm_info ("Simple connect state (%d/%d): Wait to get fully enabled", ctx->step, CONNECTION_STEP_LAST); mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_UNKNOWN, /* just a final state */ (GAsyncReadyCallback)wait_for_enabled_ready, ctx); return; case CONNECTION_STEP_REGISTER: mm_info ("Simple connect state (%d/%d): Register", ctx->step, CONNECTION_STEP_LAST); if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)) || mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) { /* 3GPP or CDMA registration */ register_in_3gpp_or_cdma_network ( ctx->self, mm_simple_connect_properties_get_operator_id (ctx->properties), (GAsyncReadyCallback)register_in_3gpp_or_cdma_network_ready, ctx); return; } /* If not 3GPP and not CDMA, this will possibly be a POTS modem, * which won't require any specific registration anywhere. * So, fall down to next step */ ctx->step++; case CONNECTION_STEP_BEARER: { MMBearerList *list = NULL; MMBearerProperties *bearer_properties; mm_info ("Simple connect state (%d/%d): Bearer", ctx->step, CONNECTION_STEP_LAST); g_object_get (ctx->self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) { g_dbus_method_invocation_return_error ( ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get the bearer list"); connection_context_free (ctx); return; } bearer_properties = mm_simple_connect_properties_get_bearer_properties (ctx->properties); /* Check if the bearer we want to create is already in the list */ ctx->bearer = mm_bearer_list_find (list, bearer_properties); if (!ctx->bearer) { mm_dbg ("Creating new bearer..."); /* If we don't have enough space to create the bearer, try to remove * a disconnected bearer first. */ if (mm_bearer_list_get_max (list) == mm_bearer_list_get_count (list)) { BearerListFindContext foreach_ctx; foreach_ctx.found = NULL; mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_list_find_disconnected, &foreach_ctx); /* Found a disconnected bearer, remove it */ if (foreach_ctx.found) { GError *error = NULL; if (!mm_bearer_list_delete_bearer (list, mm_bearer_get_path (foreach_ctx.found), &error)) { mm_dbg ("Couldn't delete disconnected bearer at '%s': '%s'", mm_bearer_get_path (foreach_ctx.found), error->message); g_error_free (error); } else mm_dbg ("Deleted disconnected bearer at '%s'", mm_bearer_get_path (foreach_ctx.found)); g_object_unref (foreach_ctx.found); } /* Re-check space, and if we still are in max, return an error */ if (mm_bearer_list_get_max (list) == mm_bearer_list_get_count (list)) { g_dbus_method_invocation_return_error ( ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY, "Cannot create new bearer: all existing bearers are connected"); connection_context_free (ctx); g_object_unref (list); g_object_unref (bearer_properties); return; } } mm_iface_modem_create_bearer (MM_IFACE_MODEM (ctx->self), bearer_properties, (GAsyncReadyCallback)create_bearer_ready, ctx); g_object_unref (list); g_object_unref (bearer_properties); return; } mm_dbg ("Using already existing bearer at '%s'...", mm_bearer_get_path (ctx->bearer)); g_object_unref (list); g_object_unref (bearer_properties); /* Fall down to next step */ ctx->step++; } case CONNECTION_STEP_CONNECT: mm_info ("Simple connect state (%d/%d): Connect", ctx->step, CONNECTION_STEP_LAST); /* Wait... if we're already using an existing bearer, we need to check if it is * already connected; and if so, just don't do anything else */ if (mm_bearer_get_status (ctx->bearer) != MM_BEARER_STATUS_CONNECTED) { mm_bearer_connect (ctx->bearer, (GAsyncReadyCallback)connect_bearer_ready, ctx); return; } mm_dbg ("Bearer at '%s' is already connected...", mm_bearer_get_path (ctx->bearer)); /* Fall down to next step */ ctx->step++; case CONNECTION_STEP_LAST: mm_info ("Simple connect state (%d/%d): All done", ctx->step, CONNECTION_STEP_LAST); /* All done, yey! */ mm_gdbus_modem_simple_complete_connect ( ctx->skeleton, ctx->invocation, mm_bearer_get_path (ctx->bearer)); connection_context_free (ctx); return; } g_assert_not_reached (); } static void connect_auth_ready (MMBaseModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; MMModemState current = MM_MODEM_STATE_UNKNOWN; if (!mm_base_modem_authorize_finish (self, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } ctx->properties = mm_simple_connect_properties_new_from_dictionary (ctx->dictionary, &error); if (!ctx->properties) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* We may be able to skip some steps, so check that before doing anything */ g_object_get (self, MM_IFACE_MODEM_STATE, ¤t, NULL); mm_info ("Simple connect started..."); /* Log about all the parameters being used for the simple connect */ { MMBearerAllowedAuth allowed_auth; gchar *str; MMBearerIpFamily ip_family; #define VALIDATE_UNSPECIFIED(str) (str ? str : "unspecified") mm_dbg (" PIN: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_pin (ctx->properties))); mm_dbg (" Operator ID: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_operator_id (ctx->properties))); mm_dbg (" Allowed roaming: %s", mm_simple_connect_properties_get_allow_roaming (ctx->properties) ? "yes" : "no"); mm_dbg (" APN: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_apn (ctx->properties))); ip_family = mm_simple_connect_properties_get_ip_type (ctx->properties); if (ip_family != MM_BEARER_IP_FAMILY_NONE) { str = mm_bearer_ip_family_build_string_from_mask (ip_family); mm_dbg (" IP family: %s", str); g_free (str); } else mm_dbg (" IP family: %s", VALIDATE_UNSPECIFIED (NULL)); allowed_auth = mm_simple_connect_properties_get_allowed_auth (ctx->properties); if (allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) { str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth); mm_dbg (" Allowed authentication: %s", str); g_free (str); } else mm_dbg (" Allowed authentication: %s", VALIDATE_UNSPECIFIED (NULL)); mm_dbg (" User: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_user (ctx->properties))); mm_dbg (" Password: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_password (ctx->properties))); mm_dbg (" Number: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_number (ctx->properties))); #undef VALIDATE_UNSPECIFIED } switch (current) { case MM_MODEM_STATE_FAILED: case MM_MODEM_STATE_UNKNOWN: case MM_MODEM_STATE_LOCKED: /* If we need unlocking, start from the very beginning */ ctx->step = CONNECTION_STEP_FIRST; break; case MM_MODEM_STATE_INITIALIZING: case MM_MODEM_STATE_DISABLING: /* If we are transitioning to the DISABLED (initialized) state, * wait to get there before going on */ ctx->step = CONNECTION_STEP_WAIT_FOR_INITIALIZED; break; case MM_MODEM_STATE_DISABLED: ctx->step = CONNECTION_STEP_ENABLE; break; case MM_MODEM_STATE_ENABLING: case MM_MODEM_STATE_DISCONNECTING: /* If we are transitioning to the ENABLED/REGISTERED state, * wait to get there before going on */ ctx->step = CONNECTION_STEP_WAIT_FOR_ENABLED; break; case MM_MODEM_STATE_ENABLED: case MM_MODEM_STATE_SEARCHING: case MM_MODEM_STATE_REGISTERED: case MM_MODEM_STATE_CONNECTING: case MM_MODEM_STATE_CONNECTED: ctx->step = CONNECTION_STEP_ENABLE + 1; break; } connection_step (ctx); } static gboolean handle_connect (MmGdbusModemSimple *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModemSimple *self) { ConnectionContext *ctx; ctx = g_new0 (ConnectionContext, 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_DEVICE_CONTROL, (GAsyncReadyCallback)connect_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MMIfaceModemSimple *self; MmGdbusModemSimple *skeleton; GDBusMethodInvocation *invocation; gchar *bearer_path; GList *bearers; MMBearer *current; } DisconnectionContext; static void disconnection_context_free (DisconnectionContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->bearer_path); if (ctx->current) g_object_unref (ctx->current); g_list_free_full (ctx->bearers, (GDestroyNotify) g_object_unref); g_free (ctx); } static void disconnect_next_bearer (DisconnectionContext *ctx); static void disconnect_ready (MMBearer *bearer, GAsyncResult *res, DisconnectionContext *ctx) { GError *error = NULL; if (!mm_bearer_disconnect_finish (bearer, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); disconnection_context_free (ctx); return; } disconnect_next_bearer (ctx); } static void disconnect_next_bearer (DisconnectionContext *ctx) { if (ctx->current) g_clear_object (&ctx->current); /* No more bearers? all done! */ if (!ctx->bearers) { mm_gdbus_modem_simple_complete_disconnect (ctx->skeleton, ctx->invocation); disconnection_context_free (ctx); return; } ctx->current = MM_BEARER (ctx->bearers->data); ctx->bearers = g_list_delete_link (ctx->bearers, ctx->bearers); mm_bearer_disconnect (ctx->current, (GAsyncReadyCallback)disconnect_ready, ctx); } static void build_connected_bearer_list (MMBearer *bearer, DisconnectionContext *ctx) { if (!ctx->bearer_path || g_str_equal (ctx->bearer_path, mm_bearer_get_path (bearer))) ctx->bearers = g_list_prepend (ctx->bearers, g_object_ref (bearer)); } static void disconnect_auth_ready (MMBaseModem *self, GAsyncResult *res, DisconnectionContext *ctx) { GError *error = NULL; MMBearerList *list = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); disconnection_context_free (ctx); return; } g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) { g_dbus_method_invocation_return_error ( ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get the bearer list"); disconnection_context_free (ctx); return; } mm_bearer_list_foreach (list, (MMBearerListForeachFunc)build_connected_bearer_list, ctx); g_object_unref (list); if (ctx->bearer_path && !ctx->bearers) { g_dbus_method_invocation_return_error ( ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't disconnect bearer '%s': not found", ctx->bearer_path); disconnection_context_free (ctx); return; } /* Go on disconnecting bearers */ disconnect_next_bearer (ctx); } static gboolean handle_disconnect (MmGdbusModemSimple *skeleton, GDBusMethodInvocation *invocation, const gchar *bearer_path, MMIfaceModemSimple *self) { DisconnectionContext *ctx; ctx = g_new0 (DisconnectionContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); if (bearer_path && bearer_path[0] == '/' && bearer_path[1]) { ctx->bearer_path = g_strdup (ctx->bearer_path); } mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)disconnect_auth_ready, ctx); return TRUE; } /*****************************************************************************/ static gboolean handle_get_status (MmGdbusModemSimple *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemSimple *self) { MMSimpleStatus *status = NULL; GVariant *dictionary; g_object_get (self, MM_IFACE_MODEM_SIMPLE_STATUS, &status, NULL); dictionary = mm_simple_status_get_dictionary (status); mm_gdbus_modem_simple_complete_get_status (skeleton, invocation, dictionary); g_variant_unref (dictionary); g_object_unref (status); return TRUE; } /*****************************************************************************/ void mm_iface_modem_simple_initialize (MMIfaceModemSimple *self) { MmGdbusModemSimple *skeleton = NULL; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_simple_skeleton_new (); g_object_set (self, MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, skeleton, NULL); /* Handle method invocations */ g_signal_connect (skeleton, "handle-connect", G_CALLBACK (handle_connect), self); g_signal_connect (skeleton, "handle-disconnect", G_CALLBACK (handle_disconnect), self); g_signal_connect (skeleton, "handle-get-status", G_CALLBACK (handle_get_status), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_simple (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_SIMPLE (skeleton)); } g_object_unref (skeleton); } void mm_iface_modem_simple_shutdown (MMIfaceModemSimple *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_simple (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_simple_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_SIMPLE_DBUS_SKELETON, "Simple DBus skeleton", "DBus skeleton for the Simple interface", MM_GDBUS_TYPE_MODEM_SIMPLE_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_SIMPLE_STATUS, "Simple status", "Compilation of status values", MM_TYPE_SIMPLE_STATUS, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_simple_get_type (void) { static GType iface_modem_simple_type = 0; if (!G_UNLIKELY (iface_modem_simple_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemSimple), /* class_size */ iface_modem_simple_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_simple_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemSimple", &info, 0); g_type_interface_add_prerequisite (iface_modem_simple_type, MM_TYPE_IFACE_MODEM); } return iface_modem_simple_type; }