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.
This commit is contained in:
Marco Trevisan (Treviño) 2021-01-28 12:54:12 +01:00
parent 2f7c78eb97
commit 27a62443a1
3 changed files with 64 additions and 11 deletions

View file

@ -84,6 +84,7 @@ struct _FpDeviceVirtualDevice
gboolean supports_cancellation; gboolean supports_cancellation;
gboolean injected_synthetic_cmd; gboolean injected_synthetic_cmd;
gboolean ignore_wait; gboolean ignore_wait;
gboolean keep_alive;
}; };
/* Not really final here, but we can do this to share the FpDeviceVirtualDevice /* Not really final here, but we can do this to share the FpDeviceVirtualDevice

View file

@ -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_ENROLL_STAGES_PREFIX "SET_ENROLL_STAGES "
#define SET_SCAN_TYPE_PREFIX "SET_SCAN_TYPE " #define SET_SCAN_TYPE_PREFIX "SET_SCAN_TYPE "
#define SET_CANCELLATION_PREFIX "SET_CANCELLATION_ENABLED " #define SET_CANCELLATION_PREFIX "SET_CANCELLATION_ENABLED "
#define SET_KEEP_ALIVE_PREFIX "SET_KEEP_ALIVE "
#define LIST_CMD "LIST" #define LIST_CMD "LIST"
#define UNPLUG_CMD "UNPLUG" #define UNPLUG_CMD "UNPLUG"
@ -289,6 +290,13 @@ recv_instruction_cb (GObject *source_object,
g_debug ("Cancellation support toggled: %d", g_debug ("Cancellation support toggled: %d",
self->supports_cancellation); 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 else
{ {
g_ptr_array_add (self->pending_commands, g_steal_pointer (&cmd)); g_ptr_array_add (self->pending_commands, g_steal_pointer (&cmd));
@ -692,6 +700,14 @@ dev_cancel (FpDevice *dev)
maybe_continue_current_action (self); 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 static void
dev_deinit (FpDevice *dev) 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->wait_command_id, g_source_remove);
g_clear_handle_id (&self->sleep_timeout_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); if (!self->keep_alive)
g_clear_object (&self->listener); stop_listener (self);
g_clear_object (&self->listener);
fpi_device_close_complete (dev, NULL); fpi_device_close_complete (dev, NULL);
} }
@ -725,6 +740,7 @@ fpi_device_virtual_device_finalize (GObject *object)
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (object); FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (object);
G_DEBUG_HERE (); G_DEBUG_HERE ();
stop_listener (self);
g_clear_pointer (&self->pending_commands, g_ptr_array_unref); g_clear_pointer (&self->pending_commands, g_ptr_array_unref);
G_OBJECT_CLASS (fpi_device_virtual_device_parent_class)->finalize (object); G_OBJECT_CLASS (fpi_device_virtual_device_parent_class)->finalize (object);
} }

View file

@ -123,7 +123,7 @@ class VirtualDeviceBase(unittest.TestCase):
def send_command(self, command, *args): def send_command(self, command, *args):
self.assertIn(command, ['INSERT', 'REMOVE', 'SCAN', 'ERROR', 'RETRY', self.assertIn(command, ['INSERT', 'REMOVE', 'SCAN', 'ERROR', 'RETRY',
'FINGER', 'UNPLUG', 'SLEEP', 'SET_ENROLL_STAGES', 'SET_SCAN_TYPE', '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: with Connection(self.sockaddr) as con:
params = ' '.join(str(p) for p in args) params = ' '.join(str(p) for p in args)
@ -162,6 +162,9 @@ class VirtualDeviceBase(unittest.TestCase):
else: else:
raise Exception('No known type found for {}'.format(obj)) 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): def send_sleep(self, interval):
self.assertGreater(interval, 0) self.assertGreater(interval, 0)
multiplier = 5 if 'UNDER_VALGRIND' in os.environ else 1 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(), self.assertTrue(error.exception.matches(FPrint.DeviceError.quark(),
FPrint.DeviceError.PROTO)) 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): def test_delayed_open(self):
self.send_command('IGNORED_COMMAND') # This will be consumed by close self.send_command('IGNORED_COMMAND') # This will be consumed by close
self.send_sleep(500) # This will be consumed by open self.send_sleep(500) # This will be consumed by open
@ -386,6 +400,28 @@ class VirtualDevice(VirtualDeviceBase):
while not opened: while not opened:
ctx.iteration(True) 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): def test_enroll(self):
matching = self.enroll_print('testprint', FPrint.Finger.LEFT_LITTLE) matching = self.enroll_print('testprint', FPrint.Finger.LEFT_LITTLE)
self.assertEqual(matching.get_username(), 'testuser') self.assertEqual(matching.get_username(), 'testuser')
@ -485,6 +521,7 @@ class VirtualDevice(VirtualDeviceBase):
ctx.iteration(True) ctx.iteration(True)
self.assertEqual(enrolled.get_driver(), self.dev.get_driver()) self.assertEqual(enrolled.get_driver(), self.dev.get_driver())
self.assertEqual(enrolled.props.fpi_data.unpack(), 'print-id')
def test_enroll_script(self): def test_enroll_script(self):
self.send_command('SET_ENROLL_STAGES', 8) self.send_command('SET_ENROLL_STAGES', 8)
@ -507,6 +544,7 @@ class VirtualDevice(VirtualDeviceBase):
enrolled = self.dev.enroll_sync(FPrint.Print.new(self.dev)) enrolled = self.dev.enroll_sync(FPrint.Print.new(self.dev))
self.assertEqual(enrolled.get_driver(), self.dev.get_driver()) self.assertEqual(enrolled.get_driver(), self.dev.get_driver())
self.assertEqual(enrolled.props.fpi_data.unpack(), 'print-id')
def test_finger_status(self): def test_finger_status(self):
self.start_verify(FPrint.Print.new(self.dev), self.start_verify(FPrint.Print.new(self.dev),
@ -812,15 +850,13 @@ class VirtualDeviceBusyDeviceOperations(VirtualDeviceBase):
super().tearDown() super().tearDown()
def test_open(self): def test_open(self):
self.send_command('IGNORED_COMMAND') self.set_keep_alive(True)
self.send_sleep(100)
with GLibErrorMessage('libfprint-virtual_device', while self.dev.is_open():
GLib.LogLevelFlags.LEVEL_WARNING, 'Could not process command: *'): ctx.iteration(True)
while self.dev.is_open():
ctx.iteration(True)
self.assertFalse(self.dev.is_open()) self.assertFalse(self.dev.is_open())
self.send_sleep(100)
self.dev.open() self.dev.open()
with self.assertRaises(GLib.Error) as error: with self.assertRaises(GLib.Error) as error:
self.dev.open_sync() self.dev.open_sync()