Commit 94baaea8 authored by Guido Günther's avatar Guido Günther

Initial wwan plugin

Heavily based on code from nm-applet.

Follow-Ups:
   - Allow to store SIM in keyring
   - Handle PUKs? (or do that in g-c-c)
parent 62981b3c
Pipeline #100944 passed with stages
in 4 minutes
......@@ -12,6 +12,10 @@ schemas = [
'org.gnome.settings-daemon.plugins.xsettings.gschema.xml'
]
if enable_wwan
schemas += 'org.gnome.settings-daemon.plugins.wwan.gschema.xml'
endif
schema_conf = configuration_data()
schema_conf.set('GETTEXT_PACKAGE', meson.project_name())
......
<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
<schema gettext-domain="@GETTEXT_PACKAGE@" id="org.gnome.settings-daemon.plugins.wwan" path="/org/gnome/settings-daemon/plugins/wwan/">
<key name="unlock-sim" type="b">
<default>false</default>
<summary>Unlock sim cards</summary>
<description>Unlock any sim cards right away.</description>
</key>
</schema>
</schemalist>
......@@ -191,6 +191,13 @@ if enable_rfkill
endif
endif
# wwan
enable_wwan = get_option('wwan')
if enable_wwan
gcr_base_dep = dependency('gcr-base-3', version: '>= 3.7.5')
mm_glib_dep = dependency('mm-glib', version: '>= 1.0')
endif
# Sharing plugin
enable_network_manager = get_option('network_manager')
assert(enable_network_manager or not host_is_linux, 'NetworkManager support is not optional on Linux platforms')
......
......@@ -8,3 +8,4 @@ option('network_manager', type: 'boolean', value: true, description: 'build with
option('rfkill', type: 'boolean', value: true, description: 'build with rfkill support (not optional on Linux platforms)')
option('smartcard', type: 'boolean', value: true, description: 'build with smartcard support')
option('wayland', type: 'boolean', value: true, description: 'build with Wayland support')
option('wwan', type: 'boolean', value: true, description: 'build with WWAN support')
......@@ -29,6 +29,10 @@ if enable_rfkill
enabled_plugins += [['rfkill', 'Rfkill']]
endif
if enable_wwan
enabled_plugins += [['wwan', 'Wwan']]
endif
plugins_conf = configuration_data()
plugins_conf.set('libexecdir', gsd_libexecdir)
......
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2019 Purism SPC
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Guido Günther <agx@sigxcpu.org>
*
*/
#include "config.h"
#include <gio/gio.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <libmm-glib.h>
#include "gsd-wwan-device.h"
struct _GsdWwanDevice
{
GObject parent;
MMModem *mm_modem;
MMSim *mm_sim;
MMObject *mm_object;
};
enum {
PROP_0,
PROP_MM_OBJECT,
PROP_MM_MODEM,
PROP_MM_SIM,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
enum {
SIM_NEEDS_UNLOCK,
N_SIGNALS
};
static guint signals[N_SIGNALS];
G_DEFINE_TYPE (GsdWwanDevice, gsd_wwan_device, G_TYPE_OBJECT)
static void
modem_get_sim_ready (MMModem *modem, GAsyncResult *res, GsdWwanDevice *self)
{
self->mm_sim = mm_modem_get_sim_finish (modem, res, NULL);
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_MM_SIM]);
g_return_if_fail (MM_IS_SIM (self->mm_sim));
g_debug ("Need to unlock sim %s (%s)",
mm_sim_get_path (self->mm_sim),
mm_sim_get_identifier (self->mm_sim));
g_signal_emit(self, signals[SIM_NEEDS_UNLOCK], 0);
}
static void
fetch_modem_info (GsdWwanDevice *self)
{
self->mm_modem = mm_object_get_modem (MM_OBJECT(self->mm_object));
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_MM_MODEM]);
g_return_if_fail (self->mm_modem);
g_debug ("Found modem %s (%s)",
mm_modem_get_path (self->mm_modem),
mm_modem_get_device (self->mm_modem));
if (mm_modem_get_state (self->mm_modem) != MM_MODEM_STATE_LOCKED)
return;
/* The sim card will be valid as long as the modem exists */
mm_modem_get_sim (self->mm_modem, NULL, (GAsyncReadyCallback)modem_get_sim_ready, self);
}
static void
gsd_wwan_device_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GsdWwanDevice *self = GSD_WWAN_DEVICE (object);
switch (prop_id) {
case PROP_MM_OBJECT:
self->mm_object = g_value_dup_object (value);
fetch_modem_info (self);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsd_wwan_device_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GsdWwanDevice *self = GSD_WWAN_DEVICE (object);
switch (prop_id) {
case PROP_MM_OBJECT:
g_value_set_object (value, self->mm_object);
break;
case PROP_MM_MODEM:
g_value_set_object (value, self->mm_modem);
break;
case PROP_MM_SIM:
g_value_set_object (value, self->mm_sim);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsd_wwan_device_dispose (GObject *object)
{
GsdWwanDevice *self = GSD_WWAN_DEVICE (object);
g_clear_object (&self->mm_modem);
g_clear_object (&self->mm_sim);
g_clear_object (&self->mm_object);
G_OBJECT_CLASS (gsd_wwan_device_parent_class)->dispose (object);
}
static void
gsd_wwan_device_class_init (GsdWwanDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = gsd_wwan_device_get_property;
object_class->set_property = gsd_wwan_device_set_property;
object_class->dispose = gsd_wwan_device_dispose;
signals[SIM_NEEDS_UNLOCK] =
g_signal_new ("sim-needs-unlock",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
props[PROP_MM_OBJECT] =
g_param_spec_object ("mm-object",
"mm-object",
"The MMObject representing a modem",
MM_TYPE_OBJECT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_MM_MODEM] =
g_param_spec_object ("mm-modem",
"mm-modem",
"The MMModem interface object",
MM_TYPE_MODEM,
G_PARAM_READABLE |
G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS);
props[PROP_MM_SIM] =
g_param_spec_object ("mm-sim",
"mm-sim",
"The MMSim interface object",
MM_TYPE_SIM,
G_PARAM_READABLE |
G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
}
static void
gsd_wwan_device_init (GsdWwanDevice *self)
{
}
GsdWwanDevice *
gsd_wwan_device_new (MMObject *object)
{
return g_object_new (GSD_TYPE_WWAN_DEVICE, "mm-object", object, NULL);
}
MMObject *
gsd_wwan_device_get_mm_object (GsdWwanDevice *self)
{
g_return_val_if_fail (GSD_IS_WWAN_DEVICE (self), NULL);
return self->mm_object;
}
MMModem *
gsd_wwan_device_get_mm_modem (GsdWwanDevice *self)
{
g_return_val_if_fail (GSD_IS_WWAN_DEVICE (self), NULL);
return self->mm_modem;
}
MMSim *
gsd_wwan_device_get_mm_sim (GsdWwanDevice *self)
{
g_return_val_if_fail (GSD_IS_WWAN_DEVICE (self), NULL);
return self->mm_sim;
}
gboolean
gsd_wwan_device_needs_unlock (GsdWwanDevice *self)
{
g_return_val_if_fail (GSD_IS_WWAN_DEVICE (self), FALSE);
return (mm_modem_get_state (self->mm_modem) == MM_MODEM_STATE_LOCKED &&
self->mm_sim);
}
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2019 Purism SPC
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Guido Günther <agx@sigxcpu.org>
*
*/
# pragma once
#include <glib-object.h>
#include <libmm-glib.h>
G_BEGIN_DECLS
#define GSD_TYPE_WWAN_DEVICE (gsd_wwan_device_get_type())
G_DECLARE_FINAL_TYPE (GsdWwanDevice, gsd_wwan_device, GSD, WWAN_DEVICE, GObject)
GsdWwanDevice *gsd_wwan_device_new (MMObject *object);
MMObject *gsd_wwan_device_get_mm_object (GsdWwanDevice *self);
MMModem *gsd_wwan_device_get_mm_modem (GsdWwanDevice *self);
MMSim *gsd_wwan_device_get_mm_sim (GsdWwanDevice *self);
gboolean gsd_wwan_device_needs_unlock (GsdWwanDevice *self);
G_END_DECLS
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2019 Purism SPC
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Guido Günther <agx@sigxcpu.org>
*
*/
#include "config.h"
#include <string.h>
#include <locale.h>
#include <gio/gio.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <libmm-glib.h>
#include "gnome-settings-profile.h"
#include "gsd-wwan-device.h"
#include "gsd-wwan-manager.h"
#include "gsd-wwan-pinentry.h"
struct _GsdWwanManager
{
GObject parent;
guint start_idle_id;
gboolean unlock;
GSettings *settings;
GPtrArray *devices;
MMManager *mm1;
gboolean mm1_running;
};
enum {
PROP_0,
PROP_UNLOCK_SIM,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
#define GSD_WWAN_SCHEMA_DIR "org.gnome.settings-daemon.plugins.wwan"
#define GSD_WWAN_SCHEMA_UNLOCK_SIM "unlock-sim"
G_DEFINE_TYPE (GsdWwanManager, gsd_wwan_manager, G_TYPE_OBJECT)
/* The plugin's manager object */
static gpointer manager_object = NULL;
static void
unlock_sim_cb (GsdWwanManager *self, GsdWwanDevice *device)
{
g_return_if_fail (GSD_IS_WWAN_MANAGER (self));
g_return_if_fail (GSD_IS_WWAN_DEVICE (device));
if (!self->unlock)
return;
gsd_wwan_pinentry_unlock_sim (device, NULL);
}
static gboolean
device_match_by_object (GsdWwanDevice *device, GDBusObject *object)
{
g_return_val_if_fail (G_IS_DBUS_OBJECT (object), FALSE);
g_return_val_if_fail (GSD_IS_WWAN_DEVICE (device), FALSE);
return object == G_DBUS_OBJECT (gsd_wwan_device_get_mm_object (device));
}
static void
gsd_wwan_manager_cache_mm_object (GsdWwanManager *self, MMObject *obj)
{
const gchar *modem_object_path;
GsdWwanDevice *device;
modem_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (obj));
g_return_if_fail (modem_object_path);
if (g_ptr_array_find_with_equal_func (self->devices,
obj,
(GEqualFunc) device_match_by_object,
NULL)) {
g_debug("Device %s already tracked", modem_object_path);
return;
}
g_debug ("Tracking device at: %s", modem_object_path);
device = gsd_wwan_device_new (MM_OBJECT (obj));
g_signal_connect_swapped (device,
"sim-needs-unlock",
G_CALLBACK (unlock_sim_cb),
self);
g_ptr_array_add (self->devices, device);
}
static void
object_added_cb (GsdWwanManager *self, GDBusObject *object, GDBusObjectManager *obj_manager)
{
g_return_if_fail (GSD_IS_WWAN_MANAGER (self));
g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER (obj_manager));
gsd_wwan_manager_cache_mm_object (self, MM_OBJECT(object));
}
static void
object_removed_cb (GsdWwanManager *self, GDBusObject *object, GDBusObjectManager *obj_manager)
{
guint index;
g_return_if_fail (GSD_IS_WWAN_MANAGER (self));
g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER (obj_manager));
if (g_ptr_array_find_with_equal_func (self->devices,
object,
(GEqualFunc) device_match_by_object,
&index)) {
g_ptr_array_remove_index_fast (self->devices, index);
}
}
static void
mm1_name_owner_changed_cb (GDBusObjectManagerClient *client, GParamSpec *pspec, GsdWwanManager *self)
{
g_autofree gchar *name_owner = NULL;
name_owner = g_dbus_object_manager_client_get_name_owner (client);
self->mm1_running = !!name_owner;
g_debug ("mm name owned: %d", self->mm1_running);
if (!self->mm1_running) {
/* Drop all devices when MM goes away */
if (self->devices->len) {
g_ptr_array_set_size (self->devices, 0);
}
return;
}
}
static void
get_all_modems (GsdWwanManager *self)
{
GList *list, *l;
g_return_if_fail (MM_IS_MANAGER (self->mm1));
list = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->mm1));
for (l = list; l != NULL; l = l->next)
gsd_wwan_manager_cache_mm_object (self, MM_OBJECT(l->data));
g_list_free_full (list, g_object_unref);
}
static void
mm1_manager_new_cb (GDBusConnection *connection, GAsyncResult *res, GsdWwanManager *self)
{
g_autoptr(GError) error = NULL;
self->mm1 = mm_manager_new_finish (res, &error);
if (self->mm1) {
/* Listen for added/removed modems */
g_signal_connect_object (self->mm1,
"object-added",
G_CALLBACK (object_added_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->mm1,
"object-removed",
G_CALLBACK (object_removed_cb),
self,
G_CONNECT_SWAPPED);
/* Listen for name owner changes */
g_signal_connect (self->mm1,
"notify::name-owner",
G_CALLBACK (mm1_name_owner_changed_cb),
self);
/* Handle all modems already known to MM */
get_all_modems (self);
} else {
g_warning ("Error connecting to D-Bus: %s", error->message);
}
}
static void
set_modem_manager (GsdWwanManager *self)
{
GDBusConnection *system_bus;
g_autoptr(GError) error = NULL;
system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (system_bus) {
mm_manager_new (system_bus,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
NULL,
(GAsyncReadyCallback) mm1_manager_new_cb,
self);
g_object_unref (system_bus);
} else {
g_warning ("Error connecting to system D-Bus: %s", error->message);
}
}
static gboolean
start_wwan_idle_cb (GsdWwanManager *self)
{
g_debug ("Idle starting wwan manager");
gnome_settings_profile_start (NULL);
g_return_val_if_fail(GSD_IS_WWAN_MANAGER (self), FALSE);
self->settings = g_settings_new (GSD_WWAN_SCHEMA_DIR);
g_settings_bind (self->settings, "unlock-sim", self, "unlock-sim", G_SETTINGS_BIND_GET);
set_modem_manager (self);
gnome_settings_profile_end (NULL);
self->start_idle_id = 0;
return FALSE;
}
gboolean
gsd_wwan_manager_start (GsdWwanManager *self,
GError **error)
{
g_debug ("Starting wwan manager");
g_return_val_if_fail(GSD_IS_WWAN_MANAGER (self), FALSE);
gnome_settings_profile_start (NULL);
self->start_idle_id = g_idle_add ((GSourceFunc) start_wwan_idle_cb, self);
g_source_set_name_by_id (self->start_idle_id, "[gnome-settings-daemon] start_wwan_idle_cb");
gnome_settings_profile_end (NULL);
return TRUE;
}
void
gsd_wwan_manager_stop (GsdWwanManager *self)
{
g_debug ("Stopping wwan manager");
}
static void
unlock_all (GsdWwanDevice *device, GsdWwanManager *self)
{
if (gsd_wwan_device_needs_unlock (device))
unlock_sim_cb (self, device);
}
static void
gsd_wwan_manager_set_unlock_sim (GsdWwanManager *self, gboolean unlock)
{
if (self->unlock == unlock)
return;
self->unlock = unlock;
if (self->unlock) {
g_ptr_array_foreach (self->devices,
(GFunc) unlock_all,
self);
}
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_UNLOCK_SIM]);
}
static void
gsd_wwan_manager_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GsdWwanManager *self = GSD_WWAN_MANAGER (object);
switch (prop_id) {
case PROP_UNLOCK_SIM:
gsd_wwan_manager_set_unlock_sim (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsd_wwan_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GsdWwanManager *self = GSD_WWAN_MANAGER (object);
switch (prop_id) {
case PROP_UNLOCK_SIM:
g_value_set_boolean (value, self->unlock);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsd_wwan_manager_dispose (GObject *object)
{
GsdWwanManager *self = GSD_WWAN_MANAGER (object);
if (self->mm1) {
self->mm1_running = FALSE;
g_clear_object (&self->mm1);
}
g_clear_pointer (&self->devices, g_ptr_array_unref);
g_clear_object (&self->settings);
G_OBJECT_CLASS (gsd_wwan_manager_parent_class)->dispose (object);
}
static void
gsd_wwan_manager_class_init (GsdWwanManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = gsd_wwan_manager_get_property;
object_class->set_property = gsd_wwan_manager_set_property;
object_class->dispose = gsd_wwan_manager_dispose;
props[PROP_UNLOCK_SIM] =
g_param_spec_boolean ("unlock-sim",
"unlock-sim",
"Whether to unlock new sims right away",
FALSE,
G_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
}
static void
gsd_wwan_manager_init (GsdWwanManager *self)
{
self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
}
GsdWwanManager *
gsd_wwan_manager_new (void)
{
if (manager_object != NULL) {
g_object_ref (manager_object);
} else {
manager_object = g_object_new (GSD_TYPE_WWAN_MANAGER, NULL);
g_object_add_weak_pointer (manager_object,
(gpointer *) &manager_object);
}
return GSD_WWAN_MANAGER (manager_object);
}
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2019 Purism SPC
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*