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()