Implement suspend/resume handling

This commit is contained in:
Benjamin Berg
2021-05-05 18:10:39 +02:00
parent 66e7df1105
commit 70182083a1
4 changed files with 372 additions and 1 deletions

View File

@ -475,6 +475,81 @@ _fprint_device_get_id (FprintDevice *rdev)
return priv->id;
}
static void
suspend_cb (GObject *source_obj,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
GError *error = NULL;
fp_device_suspend_finish (FP_DEVICE (source_obj), res, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
}
static void
resume_cb (GObject *source_obj,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
GError *error = NULL;
fp_device_resume_finish (FP_DEVICE (source_obj), res, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
}
void
fprint_device_suspend (FprintDevice *rdev,
GAsyncReadyCallback callback,
void *user_data)
{
GTask *task = NULL;
FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev);
/* Just forward to libfprint. */
task = g_task_new (rdev, NULL, callback, user_data);
fp_device_suspend (priv->dev, NULL, suspend_cb, task);
}
void
fprint_device_resume (FprintDevice *rdev,
GAsyncReadyCallback callback,
void *user_data)
{
GTask *task = NULL;
FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev);
/* Just forward to libfprint. */
task = g_task_new (rdev, NULL, callback, user_data);
fp_device_resume (priv->dev, NULL, resume_cb, task);
}
void
fprint_device_suspend_finish (FprintDevice *rdev,
GAsyncResult *res,
GError **error)
{
g_task_propagate_boolean (G_TASK (res), error);
}
void
fprint_device_resume_finish (FprintDevice *rdev,
GAsyncResult *res,
GError **error)
{
g_task_propagate_boolean (G_TASK (res), error);
}
static const char *
fp_finger_to_name (FpFinger finger)
{

View File

@ -95,6 +95,22 @@ struct _FprintDevice
FprintDevice *fprint_device_new (FpDevice *dev);
guint32 _fprint_device_get_id (FprintDevice *rdev);
void fprint_device_suspend (FprintDevice *rdev,
GAsyncReadyCallback callback,
void *user_data);
void fprint_device_resume (FprintDevice *rdev,
GAsyncReadyCallback callback,
void *user_data);
void fprint_device_suspend_finish (FprintDevice *rdev,
GAsyncResult *result,
GError **error);
void fprint_device_resume_finish (FprintDevice *rdev,
GAsyncResult *res,
GError **error);
/* Print */
/* TODO */

View File

@ -24,9 +24,14 @@
#include <glib/gi18n.h>
#include <fprint.h>
#include <glib-object.h>
#include <gio/gunixfdlist.h>
#include "fprintd.h"
#define LOGIND_BUS_NAME "org.freedesktop.login1"
#define LOGIND_IFACE_NAME "org.freedesktop.login1.Manager"
#define LOGIND_OBJ_PATH "/org/freedesktop/login1"
static void fprint_manager_constructed (GObject *object);
static gboolean fprint_manager_get_devices (FprintManager *manager,
GPtrArray **devices,
@ -43,6 +48,9 @@ typedef struct
FpContext *context;
gboolean no_timeout;
guint timeout_id;
gint prepare_for_sleep_pending;
guint prepare_for_sleep_id;
gint sleep_inhibit_fd;
} FprintManagerPrivate;
G_DEFINE_TYPE_WITH_CODE (FprintManager, fprint_manager, G_TYPE_OBJECT, G_ADD_PRIVATE (FprintManager))
@ -60,6 +68,10 @@ fprint_manager_finalize (GObject *object)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
if (priv->prepare_for_sleep_id)
g_dbus_connection_signal_unsubscribe (priv->connection,
priv->prepare_for_sleep_id);
g_clear_object (&priv->object_manager);
g_clear_object (&priv->dbus_manager);
g_clear_object (&priv->connection);
@ -227,6 +239,153 @@ handle_get_default_device (FprintManager *manager,
return TRUE;
}
static void
fprint_device_suspend_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
FprintManager *manager = FPRINT_MANAGER (user_data);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
/* Fetch the result (except for the NULL dummy call). */
if (source_object != NULL)
{
fprint_device_suspend_finish (FPRINT_DEVICE (source_object), res, &error);
if (error)
{
if (!g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_OPEN) &&
!g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED))
g_message ("Unexpected error while suspending device: %s", error->message);
}
}
priv->prepare_for_sleep_pending -= 1;
/* Close FD when all devices are prepared for sleeping. */
if (priv->prepare_for_sleep_pending == 0)
{
if (priv->sleep_inhibit_fd >= 0)
close (priv->sleep_inhibit_fd);
priv->sleep_inhibit_fd = -1;
g_debug ("Released delay inhibitor for sleep.");
}
g_object_unref (manager);
}
static void
logind_sleep_inhibit_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GVariant) data = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GUnixFDList) out_fd_list = NULL;
g_autoptr(FprintManager) manager = FPRINT_MANAGER (user_data);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
gint fd_offset;
data = g_dbus_connection_call_with_unix_fd_list_finish (priv->connection, &out_fd_list, res, &error);
if (!data)
{
g_warning ("Failed to install a sleep delay inhibitor: %s", error->message);
return;
}
if (priv->sleep_inhibit_fd >= 0)
close (priv->sleep_inhibit_fd);
g_debug ("Got delay inhibitor for sleep.");
g_variant_get (data, "(h)", &fd_offset);
priv->sleep_inhibit_fd = g_unix_fd_list_get (out_fd_list, fd_offset, NULL);
}
static void
handle_prepare_for_sleep_signal (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
g_autolist (GDBusObject) devices = NULL;
FprintManager *manager = FPRINT_MANAGER (user_data);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
gboolean prepare_for_sleep;
GList *l;
if (!g_variant_check_format_string (parameters, "(b)", FALSE))
{
g_warning ("Received incorrect parameter for PrepareForSleep signal");
return;
}
g_variant_get (parameters, "(b)", &prepare_for_sleep);
/* called one more time to handle the case of no devices */
if (prepare_for_sleep)
priv->prepare_for_sleep_pending = 1;
devices = g_dbus_object_manager_get_objects (priv->object_manager);
g_debug ("Preparing devices for %s", prepare_for_sleep ? "sleep" : "resume");
for (l = devices; l != NULL; l = l->next)
{
g_autoptr(FprintDevice) dev = NULL;
FprintDBusObjectSkeleton *object = l->data;
dev = fprint_dbus_object_skeleton_get_device (object);
if (prepare_for_sleep)
{
priv->prepare_for_sleep_pending += 1;
g_object_ref (manager);
fprint_device_suspend (dev, fprint_device_suspend_cb, manager);
}
else
{
fprint_device_resume (dev, NULL, NULL);
}
}
if (prepare_for_sleep)
{
/* "Notify" the initial dummy device we added, handling no devices that suspending */
g_object_ref (manager);
fprint_device_suspend_cb (NULL, NULL, manager);
}
else
{
GVariant *arg = NULL;
arg = g_variant_new ("(ssss)",
"sleep",
"net.reactivated.Fprint",
"Suspend fingerprint readers",
"delay");
/* Grab a sleep inhibitor. */
g_dbus_connection_call_with_unix_fd_list (priv->connection,
LOGIND_BUS_NAME,
LOGIND_OBJ_PATH,
LOGIND_IFACE_NAME,
"Inhibit",
arg,
G_VARIANT_TYPE ("(h)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL,
logind_sleep_inhibit_cb,
g_object_ref (manager));
}
}
static void
device_added_cb (FprintManager *manager, FpDevice *device, FpContext *context)
{
@ -290,6 +449,7 @@ device_removed_cb (FprintManager *manager, FpDevice *device, FpContext *context)
static void
fprint_manager_constructed (GObject *object)
{
g_autoptr(GVariant) param_false = NULL;
FprintManager *manager = FPRINT_MANAGER (object);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
GDBusObjectManagerServer *object_manager_server;
@ -319,6 +479,20 @@ fprint_manager_constructed (GObject *object)
g_dbus_object_manager_server_set_connection (object_manager_server,
priv->connection);
priv->prepare_for_sleep_id = g_dbus_connection_signal_subscribe (priv->connection,
LOGIND_BUS_NAME,
LOGIND_IFACE_NAME,
"PrepareForSleep",
LOGIND_OBJ_PATH,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
handle_prepare_for_sleep_signal,
manager,
NULL);
/* Fake a resume as that triggers the inhibitor to be taken. */
param_false = g_variant_new ("(b)", FALSE);
handle_prepare_for_sleep_signal (priv->connection, NULL, NULL, NULL, NULL, param_false, manager);
/* And register the signals for initial enumeration and hotplug. */
g_signal_connect_object (priv->context,
"device-added",