mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2026-05-11 13:08:13 +02:00
validity: implement emulation mode for enroll, verify, identify, list, delete, and clear storage
This commit is contained in:
parent
d3d73d717f
commit
5dbce7bd91
5 changed files with 225 additions and 7 deletions
|
|
@ -1217,6 +1217,10 @@ dev_open (FpDevice *device)
|
|||
validity_sensor_state_init (&self->sensor);
|
||||
validity_capture_state_init (&self->capture);
|
||||
|
||||
/* Emulation mode: in-memory print store for virtual sensor */
|
||||
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
|
||||
self->emulation_prints = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error))
|
||||
{
|
||||
fpi_device_open_complete (device, error);
|
||||
|
|
@ -1255,6 +1259,8 @@ dev_close (FpDevice *device)
|
|||
validity_pair_state_free (&self->pair_state);
|
||||
validity_tls_free (&self->tls);
|
||||
|
||||
g_clear_pointer (&self->emulation_prints, g_ptr_array_unref);
|
||||
|
||||
g_clear_object (&self->interrupt_cancellable);
|
||||
|
||||
g_usb_device_release_interface (fpi_device_get_usb_device (device), 0, 0, &error);
|
||||
|
|
|
|||
|
|
@ -303,6 +303,9 @@ struct _FpiDeviceValidity
|
|||
/* Bulk data buffer (EP 0x82 reads during capture/calibration) */
|
||||
guint8 *bulk_data;
|
||||
gsize bulk_data_len;
|
||||
|
||||
/* Emulation mode: in-memory print storage (element-type FpPrint) */
|
||||
GPtrArray *emulation_prints;
|
||||
};
|
||||
|
||||
/* Enrollment SSM (validity_enroll.c) */
|
||||
|
|
|
|||
|
|
@ -1553,13 +1553,51 @@ enroll_ssm_done (FpiSsm *ssm,
|
|||
fpi_device_enroll_complete (dev, g_object_ref (print), NULL);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Emulation mode: virtual enroll
|
||||
* ================================================================ */
|
||||
|
||||
static void
|
||||
emulation_enroll (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
FpPrint *print = NULL;
|
||||
|
||||
fpi_device_get_enroll_data (device, &print);
|
||||
|
||||
fpi_print_set_type (print, FPI_PRINT_RAW);
|
||||
fpi_print_set_device_stored (print, TRUE);
|
||||
|
||||
/* Store serialisable data so the print survives serialisation */
|
||||
g_autofree gchar *user_id = fpi_print_generate_user_id (print);
|
||||
GVariant *data = g_variant_new_string (user_id);
|
||||
|
||||
g_object_set (print, "fpi-data", data, NULL);
|
||||
|
||||
/* Simulate progress for each enroll stage */
|
||||
for (int i = 0; i < VALIDITY_ENROLL_STAGES - 1; i++)
|
||||
fpi_device_enroll_progress (device, i, NULL, NULL);
|
||||
|
||||
/* Store in emulation memory */
|
||||
g_ptr_array_add (self->emulation_prints, g_object_ref (print));
|
||||
|
||||
fpi_device_enroll_complete (device, g_object_ref (print), NULL);
|
||||
}
|
||||
|
||||
void
|
||||
validity_enroll (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
FpiSsm *ssm;
|
||||
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
if (self->emulation_prints)
|
||||
{
|
||||
emulation_enroll (device);
|
||||
return;
|
||||
}
|
||||
|
||||
ssm = fpi_ssm_new (device, enroll_run_state, ENROLL_NUM_STATES);
|
||||
fpi_ssm_start (ssm, enroll_ssm_done);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -643,6 +643,88 @@ verify_ssm_done (FpiSsm *ssm,
|
|||
self->bulk_data_len = 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Emulation mode: virtual verify / identify / list / delete / clear
|
||||
* ================================================================ */
|
||||
|
||||
static void
|
||||
emulation_verify (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
|
||||
if (self->emulation_prints->len > 0)
|
||||
fpi_device_verify_report (device, FPI_MATCH_SUCCESS, NULL, NULL);
|
||||
else
|
||||
fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL);
|
||||
|
||||
fpi_device_verify_complete (device, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
emulation_identify (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
GPtrArray *gallery = NULL;
|
||||
|
||||
fpi_device_get_identify_data (device, &gallery);
|
||||
|
||||
if (self->emulation_prints->len > 0 && gallery && gallery->len > 0)
|
||||
fpi_device_identify_report (device,
|
||||
g_ptr_array_index (gallery, 0),
|
||||
g_ptr_array_index (self->emulation_prints, 0),
|
||||
NULL);
|
||||
else
|
||||
fpi_device_identify_report (device, NULL, NULL, NULL);
|
||||
|
||||
fpi_device_identify_complete (device, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
emulation_list (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
GPtrArray *prints = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
for (guint i = 0; i < self->emulation_prints->len; i++)
|
||||
g_ptr_array_add (prints,
|
||||
g_object_ref (g_ptr_array_index (self->emulation_prints, i)));
|
||||
|
||||
fpi_device_list_complete (device, prints, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
emulation_delete (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
FpPrint *print = NULL;
|
||||
|
||||
fpi_device_get_delete_data (device, &print);
|
||||
|
||||
FpFinger target_finger = fp_print_get_finger (print);
|
||||
|
||||
for (guint i = 0; i < self->emulation_prints->len; i++)
|
||||
{
|
||||
FpPrint *stored = g_ptr_array_index (self->emulation_prints, i);
|
||||
|
||||
if (fp_print_get_finger (stored) == target_finger)
|
||||
{
|
||||
g_ptr_array_remove_index (self->emulation_prints, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fpi_device_delete_complete (device, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
emulation_clear_storage (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
|
||||
g_ptr_array_set_size (self->emulation_prints, 0);
|
||||
fpi_device_clear_storage_complete (device, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
validity_verify (FpDevice *device)
|
||||
{
|
||||
|
|
@ -651,6 +733,12 @@ validity_verify (FpDevice *device)
|
|||
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
if (self->emulation_prints)
|
||||
{
|
||||
emulation_verify (device);
|
||||
return;
|
||||
}
|
||||
|
||||
self->identify_mode = FALSE;
|
||||
|
||||
ssm = fpi_ssm_new (device, verify_run_state, VERIFY_NUM_STATES);
|
||||
|
|
@ -665,6 +753,12 @@ validity_identify (FpDevice *device)
|
|||
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
if (self->emulation_prints)
|
||||
{
|
||||
emulation_identify (device);
|
||||
return;
|
||||
}
|
||||
|
||||
self->identify_mode = TRUE;
|
||||
|
||||
ssm = fpi_ssm_new (device, verify_run_state, VERIFY_NUM_STATES);
|
||||
|
|
@ -837,11 +931,18 @@ list_ssm_done (FpiSsm *ssm,
|
|||
void
|
||||
validity_list (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
FpiSsm *ssm;
|
||||
GPtrArray *prints_array;
|
||||
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
if (self->emulation_prints)
|
||||
{
|
||||
emulation_list (device);
|
||||
return;
|
||||
}
|
||||
|
||||
prints_array = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
ssm = fpi_ssm_new (device, list_run_state, LIST_NUM_STATES);
|
||||
|
|
@ -1048,10 +1149,17 @@ delete_ssm_done (FpiSsm *ssm,
|
|||
void
|
||||
validity_delete (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
FpiSsm *ssm;
|
||||
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
if (self->emulation_prints)
|
||||
{
|
||||
emulation_delete (device);
|
||||
return;
|
||||
}
|
||||
|
||||
ssm = fpi_ssm_new (device, delete_run_state, DELETE_NUM_STATES);
|
||||
fpi_ssm_start (ssm, delete_ssm_done);
|
||||
}
|
||||
|
|
@ -1173,10 +1281,17 @@ clear_ssm_done (FpiSsm *ssm,
|
|||
void
|
||||
validity_clear_storage (FpDevice *device)
|
||||
{
|
||||
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (device);
|
||||
FpiSsm *ssm;
|
||||
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
if (self->emulation_prints)
|
||||
{
|
||||
emulation_clear_storage (device);
|
||||
return;
|
||||
}
|
||||
|
||||
ssm = fpi_ssm_new (device, clear_run_state, CLEAR_NUM_STATES);
|
||||
fpi_ssm_start (ssm, clear_ssm_done);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ import sys
|
|||
import gi
|
||||
|
||||
gi.require_version('FPrint', '2.0')
|
||||
from gi.repository import FPrint
|
||||
from gi.repository import FPrint, GLib
|
||||
|
||||
# Exit with error on any exception, including those happening in callbacks
|
||||
sys.excepthook = lambda *args: (traceback.print_exception(*args), sys.exit(1))
|
||||
|
||||
ctx = GLib.main_context_default()
|
||||
|
||||
c = FPrint.Context()
|
||||
c.enumerate()
|
||||
devices = c.get_devices()
|
||||
|
|
@ -23,7 +25,6 @@ del devices
|
|||
assert d.get_driver() == "validity", f"Expected 'validity', got '{d.get_driver()}'"
|
||||
|
||||
# Verify features detected by auto_initialize_features
|
||||
# Iteration 6 added enroll, verify, identify, list, delete, clear_storage.
|
||||
assert not d.has_feature(FPrint.DeviceFeature.CAPTURE)
|
||||
assert d.has_feature(FPrint.DeviceFeature.VERIFY)
|
||||
assert d.has_feature(FPrint.DeviceFeature.IDENTIFY)
|
||||
|
|
@ -34,14 +35,69 @@ assert d.has_feature(FPrint.DeviceFeature.STORAGE_DELETE)
|
|||
assert d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR)
|
||||
assert d.has_feature(FPrint.DeviceFeature.ALWAYS_ON)
|
||||
|
||||
# Test open (sends GET_VERSION, cmd 0x19, GET_FW_INFO) and close
|
||||
print("opening")
|
||||
d.open_sync()
|
||||
print("open done")
|
||||
|
||||
print("closing")
|
||||
# 1. Clear storage — ensure the sensor is in a clean state
|
||||
print("clearing storage")
|
||||
d.clear_storage_sync()
|
||||
print("clear done")
|
||||
|
||||
# 2. Enroll a finger
|
||||
template = FPrint.Print.new(d)
|
||||
|
||||
def enroll_progress(*args):
|
||||
print('enroll progress: ' + str(args))
|
||||
|
||||
print("enrolling")
|
||||
assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE
|
||||
p = d.enroll_sync(template, None, enroll_progress, None)
|
||||
assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE
|
||||
print("enroll done")
|
||||
|
||||
# 3. List enrolled prints — should have exactly one
|
||||
print("listing")
|
||||
stored = d.list_prints_sync()
|
||||
print("listing done")
|
||||
assert len(stored) == 1
|
||||
|
||||
# 4. Verify against the enrolled print
|
||||
print("verifying")
|
||||
assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE
|
||||
verify_res, verify_print = d.verify_sync(p)
|
||||
assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE
|
||||
print("verify done")
|
||||
del p
|
||||
assert verify_res == True
|
||||
|
||||
# 5. Identify (async) with deserialized prints
|
||||
identified = False
|
||||
|
||||
def identify_done(dev, res):
|
||||
global identified
|
||||
identified = True
|
||||
identify_match, identify_print = dev.identify_finish(res)
|
||||
print('identification done: ', identify_match, identify_print)
|
||||
assert identify_match.equal(identify_print)
|
||||
|
||||
deserialized_prints = []
|
||||
for p in stored:
|
||||
deserialized_prints.append(FPrint.Print.deserialize(p.serialize()))
|
||||
assert deserialized_prints[-1].equal(p)
|
||||
del stored
|
||||
|
||||
print("async identifying")
|
||||
d.identify(deserialized_prints, callback=identify_done)
|
||||
del deserialized_prints
|
||||
|
||||
while not identified:
|
||||
ctx.iteration(True)
|
||||
|
||||
# 6. Delete the enrolled print
|
||||
print("deleting")
|
||||
d.delete_print_sync(p)
|
||||
print("delete done")
|
||||
|
||||
d.close_sync()
|
||||
print("close done")
|
||||
|
||||
del d
|
||||
del c
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue