api,modem: new 'SetPrimarySimSlot' method

This new method allows changing the SIM slot considered as primary,
when the modem supports multiple SIM slots.

The generic handling of this method will make sure that the modem
object and all its SIM objects are re-probed from scratch as soon as a
successful SIM slot switch happens.

Implementations may report MM_CORE_ERROR_EXISTS when the switch
doesn't need to happen (e.g. if the requested SIM slot is already the
active one).
This commit is contained in:
Aleksander Morgado
2020-08-01 09:59:37 +02:00
parent 5041b9c99b
commit 924cf1af3c
8 changed files with 303 additions and 0 deletions

View File

@@ -63,6 +63,7 @@ static gchar *set_current_capabilities_str;
static gchar *set_allowed_modes_str;
static gchar *set_preferred_mode_str;
static gchar *set_current_bands_str;
static gint set_primary_sim_slot_int;
static gboolean inhibit_flag;
static GOptionEntry entries[] = {
@@ -126,6 +127,10 @@ static GOptionEntry entries[] = {
"Set bands to be used by a given modem.",
"[BAND1|BAND2...]"
},
{ "set-primary-sim-slot", 0, 0, G_OPTION_ARG_INT, &set_primary_sim_slot_int,
"Switch to the selected SIM slot",
"[SLOT NUMBER]"
},
{ "inhibit", 0, 0, G_OPTION_ARG_NONE, &inhibit_flag,
"Inhibit the modem",
NULL
@@ -173,6 +178,7 @@ mmcli_modem_options_enabled (void)
!!set_allowed_modes_str +
!!set_preferred_mode_str +
!!set_current_bands_str +
(set_primary_sim_slot_int > 0) +
inhibit_flag);
if (n_actions == 0 && mmcli_get_common_modem_string ()) {
@@ -890,6 +896,32 @@ parse_current_bands (MMModemBand **bands,
}
}
static void
set_primary_sim_slot_process_reply (gboolean result,
const GError *error)
{
if (!result) {
g_printerr ("error: couldn't request primary SIM switch: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
g_print ("successfully requested primary SIM switch in modem\n");
}
static void
set_primary_sim_slot_ready (MMModem *modem,
GAsyncResult *result)
{
gboolean operation_result;
g_autoptr(GError) error = NULL;
operation_result = mm_modem_set_primary_sim_slot_finish (modem, result, &error);
set_primary_sim_slot_process_reply (operation_result, error);
mmcli_async_operation_done ();
}
static void
state_changed (MMModem *modem,
MMModemState old_state,
@@ -1132,6 +1164,16 @@ get_modem_ready (GObject *source,
return;
}
/* Request to switch SIM? */
if (set_primary_sim_slot_int > 0) {
mm_modem_set_primary_sim_slot (ctx->modem,
set_primary_sim_slot_int,
ctx->cancellable,
(GAsyncReadyCallback)set_primary_sim_slot_ready,
NULL);
return;
}
/* Request to inhibit the modem? */
if (inhibit_flag) {
gchar *uid;
@@ -1391,5 +1433,14 @@ mmcli_modem_run_synchronous (GDBusConnection *connection)
return;
}
/* Request to switch current SIM? */
if (set_primary_sim_slot_int > 0) {
gboolean result;
result = mm_modem_set_primary_sim_slot_sync (ctx->modem, set_primary_sim_slot_int, NULL, &error);
set_primary_sim_slot_process_reply (result, error);
return;
}
g_warn_if_reached ();
}

View File

@@ -281,6 +281,12 @@ MMModemBand documentation.
An example would be: 'egsm|dcs|pcs|g850' to select all the GSM
frequency bands.
.TP
.B \-\-set\-primary\-sim\-slot=[SLOT]
Request to switch the primary SIM slot.
The given \fBSLOT\fR must be a valid slot number in the [1,N] range, where
N is the amount of SIM slots available in the system.
.TP
.B \-\-inhibit
Inhibit the specific modem from being used by ModemManager. This method
is completely equivalent to \fB\-\-inhibit\-device\fR, with the only

View File

@@ -188,6 +188,9 @@ mm_modem_get_primary_sim_slot
mm_modem_list_sim_slots
mm_modem_list_sim_slots_finish
mm_modem_list_sim_slots_sync
mm_modem_set_primary_sim_slot
mm_modem_set_primary_sim_slot_finish
mm_modem_set_primary_sim_slot_sync
<SUBSECTION Methods>
mm_modem_enable
mm_modem_enable_finish
@@ -2135,6 +2138,9 @@ mm_gdbus_modem_call_set_current_bands_sync
mm_gdbus_modem_call_set_current_capabilities
mm_gdbus_modem_call_set_current_capabilities_finish
mm_gdbus_modem_call_set_current_capabilities_sync
mm_gdbus_modem_call_set_primary_sim_slot
mm_gdbus_modem_call_set_primary_sim_slot_finish
mm_gdbus_modem_call_set_primary_sim_slot_sync
mm_gdbus_modem_call_command
mm_gdbus_modem_call_command_finish
mm_gdbus_modem_call_command_sync
@@ -2185,6 +2191,7 @@ mm_gdbus_modem_complete_reset
mm_gdbus_modem_complete_set_current_modes
mm_gdbus_modem_complete_set_current_bands
mm_gdbus_modem_complete_set_current_capabilities
mm_gdbus_modem_complete_set_primary_sim_slot
mm_gdbus_modem_interface_info
mm_gdbus_modem_override_properties
<SUBSECTION Standard>

View File

@@ -182,6 +182,24 @@
<arg name="bands" type="au" direction="in" />
</method>
<!--
SetPrimarySimSlot:
@sim_slot: SIM slot number to set as primary.
Selects which SIM slot to be considered as primary, on devices that expose
multiple slots in the #org.freedesktop.ModemManager1.Modem:SimSlots property.
When the switch happens the modem may require a full device reprobe, so the modem
object in DBus will get removed, and recreated once the selected SIM slot is in
use.
There is no limitation on which SIM slot to select, so the user may also set as
primary a slot that doesn't currently have any valid SIM card inserted.
-->
<method name="SetPrimarySimSlot">
<arg name="sim_slot" type="u" direction="in" />
</method>
<!--
Command:
@cmd: The command string, e.g. "AT+GCAP" or "+GCAP" (leading AT is inserted if necessary).

View File

@@ -3699,6 +3699,93 @@ mm_modem_list_sim_slots_sync (MMModem *self,
/*****************************************************************************/
/**
* mm_modem_set_primary_sim_slot_finish:
* @self: A #MMModem.
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
* mm_modem_set_primary_sim_slot().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_set_primary_sim_slot().
*
* Returns: %TRUE if the SIM slot switch has been successfully requested, %FALSE if
* @error is set.
*
* Since: 1.16
*/
gboolean
mm_modem_set_primary_sim_slot_finish (MMModem *self,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
return mm_gdbus_modem_call_set_primary_sim_slot_finish (MM_GDBUS_MODEM (self), res, error);
}
/**
* mm_modem_set_primary_sim_slot:
* @self: A #MMModem.
* @sim_slot: SIM slot number.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or
* %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to select which SIM slot to be considered as primary.
*
* When the operation is finished, @callback will be invoked in the
* <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
* of the thread you are calling this method from. You can then call
* mm_modem_set_primary_sim_slot_finish() to get the result of the operation.
*
* See mm_modem_set_primary_sim_slot_sync() for the synchronous, blocking version of
* this method.
*
* Since: 1.16
*/
void
mm_modem_set_primary_sim_slot (MMModem *self,
guint sim_slot,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (MM_IS_MODEM (self));
mm_gdbus_modem_call_set_primary_sim_slot (MM_GDBUS_MODEM (self), sim_slot, cancellable, callback, user_data);
}
/**
* mm_modem_set_primary_sim_slot_sync:
* @self: A #MMModem.
* @sim_slot: SIM slot number.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously requests to select which SIM slot to be considered as primary.
*
* The calling thread is blocked until a reply is received. See
* mm_modem_set_primary_sim_slot() for the asynchronous version of this method.
*
* Returns: %TRUE if the SIM slot switch has been successfully requested, %FALSE if
* @error is set.
*
* Since: 1.16
*/
gboolean
mm_modem_set_primary_sim_slot_sync (MMModem *self,
guint sim_slot,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
return mm_gdbus_modem_call_set_primary_sim_slot_sync (MM_GDBUS_MODEM (self), sim_slot, cancellable, error);
}
/*****************************************************************************/
static void
mm_modem_init (MMModem *self)
{

View File

@@ -360,6 +360,19 @@ GPtrArray *mm_modem_list_sim_slots_sync (MMModem *self,
GCancellable *cancellable,
GError **error);
void mm_modem_set_primary_sim_slot (MMModem *self,
guint sim_slot,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_modem_set_primary_sim_slot_finish (MMModem *self,
GAsyncResult *res,
GError **error);
gboolean mm_modem_set_primary_sim_slot_sync (MMModem *self,
guint sim_slot,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* _MM_MODEM_H_ */

View File

@@ -957,6 +957,117 @@ handle_list_bearers (MmGdbusModem *skeleton,
/*****************************************************************************/
typedef struct {
MmGdbusModem *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModem *self;
guint requested_sim_slot;
} HandleSetPrimarySimSlotContext;
static void
handle_set_primary_sim_slot_context_free (HandleSetPrimarySimSlotContext *ctx)
{
g_clear_object (&ctx->skeleton);
g_clear_object (&ctx->invocation);
g_clear_object (&ctx->self);
g_free (ctx);
}
static void
set_primary_sim_slot_ready (MMIfaceModem *self,
GAsyncResult *res,
HandleSetPrimarySimSlotContext *ctx)
{
g_autoptr(GError) error = NULL;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish (self, res, &error)) {
/* If the implementation returns EXISTS, we're already in the requested SIM slot,
* so we can safely return a success on the operation and skip the reprobing */
if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS)) {
mm_obj_warn (self, "couldn't process primary SIM update request: %s", error->message);
g_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error));
handle_set_primary_sim_slot_context_free (ctx);
return;
}
mm_obj_dbg (self, "ignoring SIM update request: %s", error->message);
} else {
/* Notify about the SIM swap, which will disable and reprobe the device.
* There is no need to update the PrimarySimSlot property, as this value will be
* reloaded automatically during the reprobe. */
mm_base_modem_process_sim_switch (MM_BASE_MODEM (self));
}
mm_gdbus_modem_complete_set_primary_sim_slot (ctx->skeleton, ctx->invocation);
handle_set_primary_sim_slot_context_free (ctx);
}
static void
handle_set_primary_sim_slot_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleSetPrimarySimSlotContext *ctx)
{
GError *error = NULL;
const gchar *const *sim_slot_paths;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_set_primary_sim_slot_context_free (ctx);
return;
}
/* If SIM switching is not implemented, report an error */
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot ||
!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot switch sim: "
"operation not supported");
handle_set_primary_sim_slot_context_free (ctx);
return;
}
/* Validate SIM slot number */
sim_slot_paths = mm_gdbus_modem_get_sim_slots (ctx->skeleton);
if (ctx->requested_sim_slot > g_strv_length ((gchar **)sim_slot_paths)) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Cannot switch sim: requested SIM slot number is out of bounds");
handle_set_primary_sim_slot_context_free (ctx);
return;
}
MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot (MM_IFACE_MODEM (self),
ctx->requested_sim_slot,
(GAsyncReadyCallback)set_primary_sim_slot_ready,
ctx);
}
static gboolean
handle_set_primary_sim_slot (MmGdbusModem *skeleton,
GDBusMethodInvocation *invocation,
guint sim_slot,
MMIfaceModem *self)
{
HandleSetPrimarySimSlotContext *ctx;
ctx = g_new0 (HandleSetPrimarySimSlotContext, 1);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
ctx->requested_sim_slot = sim_slot;
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_set_primary_sim_slot_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
void
mm_iface_modem_update_access_technologies (MMIfaceModem *self,
MMModemAccessTechnology new_access_tech,
@@ -5362,6 +5473,7 @@ interface_initialization_step (GTask *task)
"signal::handle-enable", G_CALLBACK (handle_enable), self,
"signal::handle-set-current-bands", G_CALLBACK (handle_set_current_bands), self,
"signal::handle-set-current-modes", G_CALLBACK (handle_set_current_modes), self,
"signal::handle-set-primary-sim-slot", G_CALLBACK (handle_set_primary_sim_slot), self,
NULL);
/* Finally, export the new interface, even if we got errors, but only if not

View File

@@ -354,6 +354,15 @@ struct _MMIfaceModem {
guint *primary_sim_slot,
GError **error);
/* Set primary SIM slot */
void (* set_primary_sim_slot) (MMIfaceModem *self,
guint sim_slot,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (* set_primary_sim_slot_finish) (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
/* Create bearer */
void (*create_bearer) (MMIfaceModem *self,
MMBearerProperties *properties,