From 27a62443a13cbbc55cce40b83eeba903e9ba0b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 28 Jan 2021 12:54:12 +0100 Subject: [PATCH] virtual-device: Add SET_KEEP_ALIVE command, to keep the listener up We may want to be able to talk with the device while it's closed to queue commands to be performed once it opens (could be even a script), so to do this we need to close the device first, send those commands and eventually process them. We used a trick to send an invalid command before that was ignored by release, but having the device available is just easier to handle. So, when keep alive is enabled we don't stop the listener when closing but only on actual device disposition. --- libfprint/drivers/virtual-device-private.h | 1 + libfprint/drivers/virtual-device.c | 24 +++++++++-- tests/virtual-device.py | 50 +++++++++++++++++++--- 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/libfprint/drivers/virtual-device-private.h b/libfprint/drivers/virtual-device-private.h index 75ae6589..5a9a6baa 100644 --- a/libfprint/drivers/virtual-device-private.h +++ b/libfprint/drivers/virtual-device-private.h @@ -84,6 +84,7 @@ struct _FpDeviceVirtualDevice gboolean supports_cancellation; gboolean injected_synthetic_cmd; gboolean ignore_wait; + gboolean keep_alive; }; /* Not really final here, but we can do this to share the FpDeviceVirtualDevice diff --git a/libfprint/drivers/virtual-device.c b/libfprint/drivers/virtual-device.c index af589b0c..f39df4ea 100644 --- a/libfprint/drivers/virtual-device.c +++ b/libfprint/drivers/virtual-device.c @@ -44,6 +44,7 @@ G_DEFINE_TYPE (FpDeviceVirtualDevice, fpi_device_virtual_device, FP_TYPE_DEVICE) #define SET_ENROLL_STAGES_PREFIX "SET_ENROLL_STAGES " #define SET_SCAN_TYPE_PREFIX "SET_SCAN_TYPE " #define SET_CANCELLATION_PREFIX "SET_CANCELLATION_ENABLED " +#define SET_KEEP_ALIVE_PREFIX "SET_KEEP_ALIVE " #define LIST_CMD "LIST" #define UNPLUG_CMD "UNPLUG" @@ -289,6 +290,13 @@ recv_instruction_cb (GObject *source_object, g_debug ("Cancellation support toggled: %d", self->supports_cancellation); } + else if (g_str_has_prefix (cmd, SET_KEEP_ALIVE_PREFIX)) + { + self->keep_alive = g_ascii_strtoull ( + cmd + strlen (SET_KEEP_ALIVE_PREFIX), NULL, 10) != 0; + + g_debug ("Keep alive toggled: %d", self->keep_alive); + } else { g_ptr_array_add (self->pending_commands, g_steal_pointer (&cmd)); @@ -692,6 +700,14 @@ dev_cancel (FpDevice *dev) maybe_continue_current_action (self); } +static void +stop_listener (FpDeviceVirtualDevice *self) +{ + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + g_clear_object (&self->listener); +} + static void dev_deinit (FpDevice *dev) { @@ -711,10 +727,9 @@ dev_deinit (FpDevice *dev) g_clear_handle_id (&self->wait_command_id, g_source_remove); g_clear_handle_id (&self->sleep_timeout_id, g_source_remove); - g_cancellable_cancel (self->cancellable); - g_clear_object (&self->cancellable); - g_clear_object (&self->listener); - g_clear_object (&self->listener); + + if (!self->keep_alive) + stop_listener (self); fpi_device_close_complete (dev, NULL); } @@ -725,6 +740,7 @@ fpi_device_virtual_device_finalize (GObject *object) FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (object); G_DEBUG_HERE (); + stop_listener (self); g_clear_pointer (&self->pending_commands, g_ptr_array_unref); G_OBJECT_CLASS (fpi_device_virtual_device_parent_class)->finalize (object); } diff --git a/tests/virtual-device.py b/tests/virtual-device.py index 0d272609..7227fd7c 100644 --- a/tests/virtual-device.py +++ b/tests/virtual-device.py @@ -123,7 +123,7 @@ class VirtualDeviceBase(unittest.TestCase): def send_command(self, command, *args): self.assertIn(command, ['INSERT', 'REMOVE', 'SCAN', 'ERROR', 'RETRY', 'FINGER', 'UNPLUG', 'SLEEP', 'SET_ENROLL_STAGES', 'SET_SCAN_TYPE', - 'SET_CANCELLATION_ENABLED', 'IGNORED_COMMAND']) + 'SET_CANCELLATION_ENABLED', 'SET_KEEP_ALIVE', 'IGNORED_COMMAND']) with Connection(self.sockaddr) as con: params = ' '.join(str(p) for p in args) @@ -162,6 +162,9 @@ class VirtualDeviceBase(unittest.TestCase): else: raise Exception('No known type found for {}'.format(obj)) + def set_keep_alive(self, value): + self.send_command('SET_KEEP_ALIVE', 1 if value else 0) + def send_sleep(self, interval): self.assertGreater(interval, 0) multiplier = 5 if 'UNDER_VALGRIND' in os.environ else 1 @@ -361,6 +364,17 @@ class VirtualDevice(VirtualDeviceBase): self.assertTrue(error.exception.matches(FPrint.DeviceError.quark(), FPrint.DeviceError.PROTO)) + def test_open_error_with_keep_alive(self): + self._close_on_teardown = False + self.set_keep_alive(True) + self.dev.close_sync() + + self.send_error(FPrint.DeviceError.PROTO) + with self.assertRaises(GLib.Error) as error: + self.dev.open_sync() + self.assertTrue(error.exception.matches(FPrint.DeviceError.quark(), + FPrint.DeviceError.PROTO)) + def test_delayed_open(self): self.send_command('IGNORED_COMMAND') # This will be consumed by close self.send_sleep(500) # This will be consumed by open @@ -386,6 +400,28 @@ class VirtualDevice(VirtualDeviceBase): while not opened: ctx.iteration(True) + def test_delayed_open_with_keep_alive(self): + self.set_keep_alive(True) + self.dev.close_sync() + + opened = False + def on_opened(dev, res): + nonlocal opened + dev.open_finish(res) + opened = True + + self.send_sleep(500) + self.dev.open(callback=on_opened) + + self.wait_timeout(10) + self.assertFalse(self.dev.is_open()) + + self.wait_timeout(10) + self.assertFalse(self.dev.is_open()) + + while not opened: + ctx.iteration(True) + def test_enroll(self): matching = self.enroll_print('testprint', FPrint.Finger.LEFT_LITTLE) self.assertEqual(matching.get_username(), 'testuser') @@ -485,6 +521,7 @@ class VirtualDevice(VirtualDeviceBase): ctx.iteration(True) self.assertEqual(enrolled.get_driver(), self.dev.get_driver()) + self.assertEqual(enrolled.props.fpi_data.unpack(), 'print-id') def test_enroll_script(self): self.send_command('SET_ENROLL_STAGES', 8) @@ -507,6 +544,7 @@ class VirtualDevice(VirtualDeviceBase): enrolled = self.dev.enroll_sync(FPrint.Print.new(self.dev)) self.assertEqual(enrolled.get_driver(), self.dev.get_driver()) + self.assertEqual(enrolled.props.fpi_data.unpack(), 'print-id') def test_finger_status(self): self.start_verify(FPrint.Print.new(self.dev), @@ -812,15 +850,13 @@ class VirtualDeviceBusyDeviceOperations(VirtualDeviceBase): super().tearDown() def test_open(self): - self.send_command('IGNORED_COMMAND') - self.send_sleep(100) + self.set_keep_alive(True) - with GLibErrorMessage('libfprint-virtual_device', - GLib.LogLevelFlags.LEVEL_WARNING, 'Could not process command: *'): - while self.dev.is_open(): - ctx.iteration(True) + while self.dev.is_open(): + ctx.iteration(True) self.assertFalse(self.dev.is_open()) + self.send_sleep(100) self.dev.open() with self.assertRaises(GLib.Error) as error: self.dev.open_sync()